Skip to content

Commit

Permalink
feat: Analyze sap.ui.core.Lib.init() call (#33)
Browse files Browse the repository at this point in the history
JIRA: CPOUI5FOUNDATION-793

Adds checks whether `Lib.init()` has been called with `apiVersion: 2`
property.

---------

Co-authored-by: Matthias Oßwald <[email protected]>
Co-authored-by: Merlin Beutlberger <[email protected]>
  • Loading branch information
3 people authored Mar 25, 2024
1 parent 3c2d0a2 commit 6d5bcdb
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 0 deletions.
69 changes: 69 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,74 @@ export default class FileLinter {
});
}

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

analyzeLibInitCall(node: ts.CallExpression) {
if (!ts.isIdentifier(node.expression) && // Assignment `const LibInit = Library.init` and destructuring
!ts.isPropertyAccessExpression(node.expression) && /* Lib.init() */
!ts.isElementAccessExpression(node.expression) /* Lib["init"]() */) {
return;
}

const nodeExp = node.expression;
const nodeType = this.#checker.getTypeAtLocation(nodeExp);
if (!nodeType.symbol || nodeType.symbol.getName() !== "init") {
return;
}

const moduleDeclaration = this.getSymbolModuleDeclaration(nodeType.symbol);
if (!moduleDeclaration || moduleDeclaration.name.text !== "sap/ui/core/Lib") {
return;
}

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

let nodeToHighlight;

if (!initArg) {
nodeToHighlight = node;
} else {
const apiVersionNode = initArg.properties.find((prop) => {
return ts.isPropertyAssignment(prop) &&
ts.isIdentifier(prop.name) &&
prop.name.text === "apiVersion";
});

if (!apiVersionNode) { // No arguments or no 'apiVersion' property
nodeToHighlight = node;
} else if (ts.isPropertyAssignment(apiVersionNode) &&
apiVersionNode.initializer.getText() !== "2") { // String value would be "\"2\""
nodeToHighlight = apiVersionNode;
}
}

if (nodeToHighlight) {
let importedVarName: string;
if (ts.isIdentifier(nodeExp)) {
importedVarName = nodeExp.getText();
} else {
importedVarName = nodeExp.expression.getText() + ".init";
}

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

getDeprecationInfoForAccess(node: ts.AccessExpression): DeprecationInfo | null {
let symbol;
if (ts.isPropertyAccessExpression(node)) {
Expand Down
42 changes: 42 additions & 0 deletions test/fixtures/linter/rules/NoDeprecatedApi/library.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*!
* ${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: 1
});

const LibInit = Library.init;
LibInit({
apiVersion: 1
});

const {init} = Library;
init({
apiVersion: 1
});

const {init: intRenames} = Library;
intRenames({
apiVersion: 1
});
});
36 changes: 36 additions & 0 deletions test/fixtures/linter/rules/NoDeprecatedApi/library_negative.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*!
* ${copyright}
*/
sap.ui.define([
"sap/ui/core/Lib",
], function (Library) {
"use strict";

Library.init({
apiVersion: 2
});

Library["init"]({
apiVersion: 2
});

// Should be ignored
Library.load({
apiVersion: 23
});

const LibInit = Library.init;
LibInit({
apiVersion: 2
});

const {init} = Library;
init({
apiVersion: 2
});

const {init: intRenames} = Library;
intRenames({
apiVersion: 2
});
});
130 changes: 130 additions & 0 deletions test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,136 @@ Generated by [AVA](https://avajs.dev).
},
]

## General: library.js

> Snapshot 1
[
{
coverageInfo: [],
errorCount: 11,
fatalErrorCount: 0,
filePath: 'library.js',
messages: [
{
column: 2,
fatal: undefined,
line: 9,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
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}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
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}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
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}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 3,
fatal: undefined,
line: 16,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 3,
fatal: undefined,
line: 19,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 3,
fatal: undefined,
line: 22,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 3,
fatal: undefined,
line: 25,
message: 'Call to Library.init() must be declared with property {apiVersion: 2}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 3,
fatal: undefined,
line: 30,
message: 'Call to LibInit() must be declared with property {apiVersion: 2}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 3,
fatal: undefined,
line: 35,
message: 'Call to init() must be declared with property {apiVersion: 2}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
{
column: 3,
fatal: undefined,
line: 40,
message: 'Call to intRenames() must be declared with property {apiVersion: 2}',
messageDetails: 'Lib.init (https://ui5.sap.com/1.120/#/api/sap.ui.core.Lib)',
ruleId: 'ui5-linter-no-partially-deprecated-api',
severity: 2,
},
],
warningCount: 0,
},
]

## General: library_negative.js

> Snapshot 1
[
{
coverageInfo: [],
errorCount: 0,
fatalErrorCount: 0,
filePath: 'library_negative.js',
messages: [],
warningCount: 0,
},
]

## General: manifest.json

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

0 comments on commit 6d5bcdb

Please sign in to comment.