Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Analyze sap.ui.core.Lib.init() call #33

Merged
merged 36 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7b24c53
feat: Analyze sap.ui.core.Lib.init() call
d3xter666 Mar 14, 2024
32a3c65
fix: Eslint findings
d3xter666 Mar 14, 2024
d4c150c
feat: Resolve lib dependency
d3xter666 Mar 15, 2024
cc10209
feat: Add tests
d3xter666 Mar 15, 2024
4fa1096
fix: Provide better ts types
d3xter666 Mar 18, 2024
c310c64
fix: Add docs and enhance tests
d3xter666 Mar 18, 2024
03bb267
fix: Include only Lib's init() method into the analysis
d3xter666 Mar 18, 2024
bd50889
fix: Update doc
d3xter666 Mar 18, 2024
7dbab5a
fix: Eslint issues
d3xter666 Mar 18, 2024
c9d25a7
fix: Eslint
d3xter666 Mar 18, 2024
eb087e0
fix: Typos
d3xter666 Mar 19, 2024
6444db0
fix: Typos
d3xter666 Mar 19, 2024
6dd8540
fix: Rename var
d3xter666 Mar 19, 2024
7fcad3d
refactor: Lib.init analyzer
d3xter666 Mar 20, 2024
6675826
fix: Lint & snapshots
d3xter666 Mar 21, 2024
8e99eb6
fix: Merge conflicts
d3xter666 Mar 21, 2024
882d220
refactor: Check name of ModuleDeclaration instead of ImportDeclaration
matz3 Mar 21, 2024
64cda85
refactor: Better types for Lib.init() analyzer
d3xter666 Mar 21, 2024
0bbf0c4
fix: Eslint issues
d3xter666 Mar 21, 2024
04d5304
refactor: Tests
d3xter666 Mar 21, 2024
75661bd
fix: Update test snapshots
d3xter666 Mar 21, 2024
93af766
fix: Merge conflicts
d3xter666 Mar 22, 2024
c9df23f
refactor: Node availability checks
d3xter666 Mar 22, 2024
517d9c9
refactor: Node availability checks
d3xter666 Mar 22, 2024
60a957e
refactor: Node availability checks
d3xter666 Mar 22, 2024
e23e47a
refactor: Split library.js fixtures
d3xter666 Mar 22, 2024
d944e3a
fix: Add tests for ElementAccessExpression
d3xter666 Mar 22, 2024
9d0c6c8
feat: Detect destructuring and assignment of Lib.init
d3xter666 Mar 22, 2024
4fcc83a
fix: Add tests for the new scenarios
d3xter666 Mar 22, 2024
da59f34
refactor: Add comments
d3xter666 Mar 22, 2024
677f58d
refactor: Check for apiVersion: 2 only
d3xter666 Mar 25, 2024
00ba4ad
fix: Tests
d3xter666 Mar 25, 2024
e5925c0
refactor: Add comments
d3xter666 Mar 25, 2024
0bb711f
fix: Add messageDetails for Lib.init apiVersion check
matz3 Mar 22, 2024
4d3ee61
refactor: Check for analyzeLibInitCall arg's type
d3xter666 Mar 25, 2024
8f42b6e
fix: Eslint
d3xter666 Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions src/detectors/typeChecker/FileLinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default class FileLinter {
// const nodeType = this.#checker.getTypeAtLocation(node);
this.analyzePropertyAccessExpression(node as ts.CallExpression); // Check for global
this.analyzeCallExpression(node as ts.CallExpression); // Check for deprecation
this.analyzeLibInitCall(node as ts.CallExpression); // Check for sap/ui/core/Lib.init usages
} else if (node.kind === ts.SyntaxKind.PropertyAccessExpression ||
node.kind === ts.SyntaxKind.ElementAccessExpression) {
this.analyzePropertyAccessExpression(
Expand Down Expand Up @@ -211,6 +212,89 @@ export default class FileLinter {
});
}

/**
* Extracts & builds object literal
*
* @param node
* @returns {object}
*/
extractPropsRecursive = (node: ts.ObjectLiteralExpression) => {
type propsType = Record<string, {value: null | boolean | string | propsType; node: ts.Node}>;
const properties = Object.create(null) as propsType;

node.properties?.forEach((prop) => {
if (!ts.isPropertyAssignment(prop) || !prop.name) {
return;
}

const key = prop.name.getText();
if (prop.initializer.kind === ts.SyntaxKind.FalseKeyword) {
properties[key] = {value: false, node: prop.initializer};
} else if (prop.initializer.kind === ts.SyntaxKind.NullKeyword) {
properties[key] = {value: null, node: prop.initializer};
} else if (ts.isObjectLiteralExpression(prop.initializer) && prop.initializer.properties) {
properties[key] = {value: this.extractPropsRecursive(prop.initializer), node: prop.initializer};
} else if ((ts.isIdentifier(prop.initializer) ||
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
ts.isNumericLiteral(prop.initializer) ||
ts.isStringLiteral(prop.initializer)) &&
prop.initializer.text) {
properties[key] = {value: prop.initializer.getText(), node: prop.initializer};
}
});
return properties;
};

getSymbolModuleDeclaration(symbol: ts.Symbol) {
let parent = symbol.valueDeclaration?.parent;
while (parent && !ts.isModuleDeclaration(parent)) {
parent = parent.parent;
}
return parent;
}

analyzeLibInitCall(node: ts.CallExpression) {
const nodeExp = (ts.isPropertyAccessExpression(node.expression) ||
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
ts.isElementAccessExpression(node.expression)) && node.expression;
const nodeType = nodeExp && this.#checker.getTypeAtLocation(nodeExp);
if (!nodeType || nodeType.symbol?.getName() !== "init") {
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
return;
}

const moduleDeclaration = this.getSymbolModuleDeclaration(nodeType.symbol);
if (moduleDeclaration?.name.text !== "sap/ui/core/Lib") {
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
return;
}

const initArg = node?.arguments[0] &&
ts.isObjectLiteralExpression(node.arguments[0]) && node.arguments[0];

let nodeToHighlight;

if (!initArg) {
nodeToHighlight = node;
} else {
const apiKeyProp = this.extractPropsRecursive(initArg);

if (!apiKeyProp.apiVersion) {
nodeToHighlight = node;
} else if (apiKeyProp.apiVersion.value !== "2") { // String value would be "\"2\""
nodeToHighlight = apiKeyProp.apiVersion.node;
}
}

if (nodeToHighlight) {
const importedVarName = node.expression.expression.getText();

this.#reporter.addMessage({
node: nodeToHighlight,
severity: LintMessageSeverity.Error,
ruleId: "ui5-linter-no-partially-deprecated-api",
message:
`Call to ${importedVarName}.init() must be declared with property {apiVersion: 2}`,
});
}
}

getDeprecationInfoForAccess(node: ts.AccessExpression): DeprecationInfo | null {
let symbol;
if (ts.isPropertyAccessExpression(node)) {
Expand Down
32 changes: 32 additions & 0 deletions test/fixtures/linter/rules/NoDeprecatedApi/old_library.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*!
d3xter666 marked this conversation as resolved.
Show resolved Hide resolved
* ${copyright}
*/
sap.ui.define([
"sap/ui/core/Lib",
], function (Library) {
"use strict";

Library.init();
Library.init("a");
Library.init({});
Library.init({
test: 12
});
Library.init({
apiVersion: "23"
});
Library.init({
apiVersion: 11
});
Library.init({
apiVersion: "2"
});
Library.init({
apiVersion: 2
});

// Should be ignored
Library.load({
apiVersion: 23
});
});
72 changes: 72 additions & 0 deletions test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,78 @@ Generated by [AVA](https://avajs.dev).
},
]

## General: old_library.js

> Snapshot 1

[
{
coverageInfo: [],
errorCount: 7,
fatalErrorCount: 0,
filePath: 'old_library.js',
messages: [
{
column: 2,
fatal: undefined,
line: 9,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 2,
fatal: undefined,
line: 10,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 2,
fatal: undefined,
line: 11,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 2,
fatal: undefined,
line: 12,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 15,
fatal: undefined,
line: 16,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 15,
fatal: undefined,
line: 19,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 15,
fatal: undefined,
line: 22,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
],
warningCount: 0,
},
]

## General: sap.ui.jsview.js

> Snapshot 1
Expand Down
Binary file modified test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.snap
Binary file not shown.