From 34f90d2c2efc52f6faa2901e13820e04e896721a Mon Sep 17 00:00:00 2001 From: Nazarov Konstantin Date: Thu, 24 Aug 2023 00:16:37 +0300 Subject: [PATCH] [arkts linter] fix mispredict for arkts-no-standalone-this Signed-off-by: Nazarov Konstantin --- linter-4.2/src/TypeScriptLinter.ts | 75 ++++++++++++++++-------- linter-4.2/src/Utils.ts | 11 ++++ linter-4.2/test/this_type.ts | 39 ++++++++++-- linter-4.2/test/this_type.ts.relax.json | 20 +++++++ linter-4.2/test/this_type.ts.strict.json | 25 ++++++++ linter/src/TypeScriptLinter.ts | 45 ++++++++++---- linter/src/utils/TsUtils.ts | 11 ++++ linter/test/this_type.ts | 45 ++++++++++++-- linter/test/this_type.ts.relax.json | 25 ++++++++ linter/test/this_type.ts.strict.json | 30 ++++++++++ 10 files changed, 278 insertions(+), 48 deletions(-) diff --git a/linter-4.2/src/TypeScriptLinter.ts b/linter-4.2/src/TypeScriptLinter.ts index dafd72b8f..1ebaf0056 100644 --- a/linter-4.2/src/TypeScriptLinter.ts +++ b/linter-4.2/src/TypeScriptLinter.ts @@ -699,45 +699,54 @@ export class TypeScriptLinter { } private handleFunctionExpression(node: ts.Node) { - let funcExpr = node as ts.FunctionExpression; - let isGenerator = funcExpr.asteriskToken !== undefined; - let containsThis = this.functionContainsThis(funcExpr.body); - let isGeneric = funcExpr.typeParameters !== undefined && funcExpr.typeParameters.length > 0; - - let newParams = this.handleMissingParameterTypes(funcExpr); - - let [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr); - - let autofixable = !isGeneric && !isGenerator && !containsThis && - newParams && !hasUnfixableReturnType; - + const funcExpr = node as ts.FunctionExpression; + const isGenerator = funcExpr.asteriskToken !== undefined; + const containsThis = this.functionContainsThis(funcExpr.body); + const hasValidContext = this.tsUtils.hasPredecessor(funcExpr, ts.isClassLike) || + this.tsUtils.hasPredecessor(funcExpr, ts.isInterfaceDeclaration); + const isGeneric = funcExpr.typeParameters !== undefined && funcExpr.typeParameters.length > 0; + const newParams = this.handleMissingParameterTypes(funcExpr); + const [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr); + const autofixable = !isGeneric && !isGenerator && !containsThis && newParams && !hasUnfixableReturnType; let autofix: Autofix[] | undefined; if (autofixable && this.autofixesInfo.shouldAutofix(node, FaultID.FunctionExpression)) { autofix = [ Autofixer.fixFunctionExpression(funcExpr, newParams, newRetTypeNode) ]; } - this.incrementCounters(node, FaultID.FunctionExpression, autofixable, autofix); - if (!newParams) this.incrementCounters(funcExpr, FaultID.ArrowFunctionWithOmittedTypes, false); - if (isGeneric) this.incrementCounters(funcExpr, FaultID.LambdaWithTypeParameters); - if (isGenerator) this.incrementCounters(funcExpr, FaultID.GeneratorFunction); - if (containsThis) this.incrementCounters(funcExpr, FaultID.FunctionContainsThis); - if (hasUnfixableReturnType) this.incrementCounters(funcExpr, FaultID.LimitedReturnTypeInference); + if (!newParams) { + this.incrementCounters(funcExpr, FaultID.ArrowFunctionWithOmittedTypes, false); + } + if (isGeneric) { + this.incrementCounters(funcExpr, FaultID.LambdaWithTypeParameters); + } + if (isGenerator) { + this.incrementCounters(funcExpr, FaultID.GeneratorFunction); + } + if (containsThis && !hasValidContext) { + this.incrementCounters(funcExpr, FaultID.FunctionContainsThis); + } + if (hasUnfixableReturnType) { + this.incrementCounters(funcExpr, FaultID.LimitedReturnTypeInference); + } } private handleArrowFunction(node: ts.Node) { - let arrowFunc = node as ts.ArrowFunction; - if (this.functionContainsThis(arrowFunc.body)) { + const arrowFunc = node as ts.ArrowFunction; + const containsThis = this.functionContainsThis(arrowFunc.body); + const hasValidContext = this.tsUtils.hasPredecessor(arrowFunc, ts.isClassLike) || + this.tsUtils.hasPredecessor(arrowFunc, ts.isInterfaceDeclaration); + if (containsThis && !hasValidContext) { this.incrementCounters(arrowFunc, FaultID.FunctionContainsThis); } - const contextType = this.tsTypeChecker.getContextualType(arrowFunc); if (!(contextType && this.tsUtils.isLibraryType(contextType))) { this.handleMissingParameterTypes(arrowFunc); - - if (!arrowFunc.type) this.handleMissingReturnType(arrowFunc); - - if (arrowFunc.typeParameters && arrowFunc.typeParameters.length > 0) + if (!arrowFunc.type) { + this.handleMissingReturnType(arrowFunc); + } + if (arrowFunc.typeParameters && arrowFunc.typeParameters.length > 0) { this.incrementCounters(node, FaultID.LambdaWithTypeParameters); + } } } @@ -1161,7 +1170,21 @@ export class TypeScriptLinter { } private handleMethodDeclaration(node: ts.Node) { - let tsMethodDecl = node as ts.MethodDeclaration; + const tsMethodDecl = node as ts.MethodDeclaration; + const hasThis = this.functionContainsThis(tsMethodDecl); + let isStatic = false + if (tsMethodDecl.modifiers) { + for (let mod of tsMethodDecl.modifiers) { + if (mod.kind === ts.SyntaxKind.StaticKeyword) { + isStatic = true; + break; + } + } + } + + if (isStatic && hasThis) { + this.incrementCounters(node, FaultID.FunctionContainsThis); + } if (!tsMethodDecl.type) this.handleMissingReturnType(tsMethodDecl); diff --git a/linter-4.2/src/Utils.ts b/linter-4.2/src/Utils.ts index e353dce55..d8fec72cd 100644 --- a/linter-4.2/src/Utils.ts +++ b/linter-4.2/src/Utils.ts @@ -587,6 +587,17 @@ export class TsUtils { return false; } + public hasPredecessor(node: ts.Node, predicate: (node: ts.Node) => boolean): boolean { + let parent = node.parent; + while (parent !== undefined) { + if (predicate(parent)) { + return true; + } + parent = parent.parent; + } + return false; + } + private processParentTypes(parentTypes: ts.NodeArray, typeB: ts.Type): boolean { for (let baseTypeExpr of parentTypes) { let baseType = this.tsTypeChecker.getTypeAtLocation(baseTypeExpr); diff --git a/linter-4.2/test/this_type.ts b/linter-4.2/test/this_type.ts index ce8847152..b95994783 100644 --- a/linter-4.2/test/this_type.ts +++ b/linter-4.2/test/this_type.ts @@ -22,19 +22,19 @@ interface Adder { } class StringAdder implements Adder { - value = ''; + value = ""; getValue(): string { return this.value; } addFoo(): this { - this.value += 'foo'; + this.value += "foo"; return this; } addBar(): this { - this.value += 'bar'; + this.value += "bar"; return this; } @@ -45,7 +45,7 @@ class StringAdder implements Adder { } const stringAdder: StringAdder = new StringAdder(); -const str = stringAdder.addFoo().addBar().addGreeting('Jane').getValue(); +const str = stringAdder.addFoo().addBar().addGreeting("Jane").getValue(); console.log(str); @@ -53,4 +53,33 @@ class C { m(c: this): void { console.log(c); } -} \ No newline at end of file +} + +class HH { + f = () => { + this; + }; + ff() { + this; + let fff = () => { + this; + }; + fff(); + } + ffff(): void { + this; + } + static fffff() { + this; + } +} + +let g = () => { + this; +}; +let gg = function () { + this; +}; +function ggg() { + this; +} diff --git a/linter-4.2/test/this_type.ts.relax.json b/linter-4.2/test/this_type.ts.relax.json index 3b9cf7c60..9d215c36e 100644 --- a/linter-4.2/test/this_type.ts.relax.json +++ b/linter-4.2/test/this_type.ts.relax.json @@ -48,6 +48,26 @@ "line": 53, "column": 8, "problem": "ThisType" + }, + { + "line": 72, + "column": 3, + "problem": "FunctionContainsThis" + }, + { + "line": 77, + "column": 9, + "problem": "FunctionContainsThis" + }, + { + "line": 80, + "column": 10, + "problem": "FunctionContainsThis" + }, + { + "line": 83, + "column": 1, + "problem": "FunctionContainsThis" } ] } \ No newline at end of file diff --git a/linter-4.2/test/this_type.ts.strict.json b/linter-4.2/test/this_type.ts.strict.json index 3b9cf7c60..673d4a501 100644 --- a/linter-4.2/test/this_type.ts.strict.json +++ b/linter-4.2/test/this_type.ts.strict.json @@ -48,6 +48,31 @@ "line": 53, "column": 8, "problem": "ThisType" + }, + { + "line": 72, + "column": 3, + "problem": "FunctionContainsThis" + }, + { + "line": 77, + "column": 9, + "problem": "FunctionContainsThis" + }, + { + "line": 80, + "column": 10, + "problem": "FunctionExpression" + }, + { + "line": 80, + "column": 10, + "problem": "FunctionContainsThis" + }, + { + "line": 83, + "column": 1, + "problem": "FunctionContainsThis" } ] } \ No newline at end of file diff --git a/linter/src/TypeScriptLinter.ts b/linter/src/TypeScriptLinter.ts index 0c9ef14d4..aeb0e3199 100644 --- a/linter/src/TypeScriptLinter.ts +++ b/linter/src/TypeScriptLinter.ts @@ -679,13 +679,15 @@ export class TypeScriptLinter { } private handleFunctionExpression(node: ts.Node) { - let funcExpr = node as ts.FunctionExpression; - let isGenerator = funcExpr.asteriskToken !== undefined; - let containsThis = functionContainsThis(funcExpr.body); - let isGeneric = funcExpr.typeParameters !== undefined && funcExpr.typeParameters.length > 0; - let newParams = this.handleMissingParameterTypes(funcExpr); - let [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr); - let autofixable = !isGeneric && !isGenerator && !containsThis && newParams && !hasUnfixableReturnType; + const funcExpr = node as ts.FunctionExpression; + const isGenerator = funcExpr.asteriskToken !== undefined; + const containsThis = functionContainsThis(funcExpr.body); + const hasValidContext = this.tsUtils.hasPredecessor(funcExpr, ts.isClassLike) || + this.tsUtils.hasPredecessor(funcExpr, ts.isInterfaceDeclaration); + const isGeneric = funcExpr.typeParameters !== undefined && funcExpr.typeParameters.length > 0; + const newParams = this.handleMissingParameterTypes(funcExpr); + const [hasUnfixableReturnType, newRetTypeNode] = this.handleMissingReturnType(funcExpr); + const autofixable = !isGeneric && !isGenerator && !containsThis && newParams && !hasUnfixableReturnType; let autofix: Autofix[] | undefined; if (autofixable && this.autofixesInfo.shouldAutofix(node, FaultID.FunctionExpression)) { autofix = [ Autofixer.fixFunctionExpression(funcExpr, newParams, newRetTypeNode) ]; @@ -700,7 +702,7 @@ export class TypeScriptLinter { if (isGenerator) { this.incrementCounters(funcExpr, FaultID.GeneratorFunction); } - if (containsThis) { + if (containsThis && !hasValidContext) { this.incrementCounters(funcExpr, FaultID.FunctionContainsThis); } if (hasUnfixableReturnType) { @@ -709,8 +711,11 @@ export class TypeScriptLinter { } private handleArrowFunction(node: ts.Node) { - let arrowFunc = node as ts.ArrowFunction; - if (functionContainsThis(arrowFunc.body)) { + const arrowFunc = node as ts.ArrowFunction; + const containsThis = functionContainsThis(arrowFunc.body); + const hasValidContext = this.tsUtils.hasPredecessor(arrowFunc, ts.isClassLike) || + this.tsUtils.hasPredecessor(arrowFunc, ts.isInterfaceDeclaration); + if (containsThis && !hasValidContext) { this.incrementCounters(arrowFunc, FaultID.FunctionContainsThis); } const contextType = this.tsTypeChecker.getContextualType(arrowFunc); @@ -1139,7 +1144,21 @@ export class TypeScriptLinter { } private handleMethodDeclaration(node: ts.Node) { - let tsMethodDecl = node as ts.MethodDeclaration; + const tsMethodDecl = node as ts.MethodDeclaration; + const hasThis = functionContainsThis(tsMethodDecl); + let isStatic = false + if (tsMethodDecl.modifiers) { + for (let mod of tsMethodDecl.modifiers) { + if (mod.kind === ts.SyntaxKind.StaticKeyword) { + isStatic = true; + break; + } + } + } + + if (isStatic && hasThis) { + this.incrementCounters(node, FaultID.FunctionContainsThis); + } if (!tsMethodDecl.type) this.handleMissingReturnType(tsMethodDecl); @@ -1157,7 +1176,9 @@ export class TypeScriptLinter { if (tsClassDecl.name) // May be undefined in `export default class { ... }`. className = tsClassDecl.name.text; - + if (functionContainsThis(node)) { + this.incrementCounters(node, FaultID.FunctionContainsThis); + } if (this.staticBlocks.has(className)) this.incrementCounters(node, FaultID.MultipleStaticBlocks); else diff --git a/linter/src/utils/TsUtils.ts b/linter/src/utils/TsUtils.ts index 45a096714..34315e7d1 100644 --- a/linter/src/utils/TsUtils.ts +++ b/linter/src/utils/TsUtils.ts @@ -449,6 +449,17 @@ export class TsUtils { return false; } + public hasPredecessor(node: ts.Node, predicate: (node: ts.Node) => boolean): boolean { + let parent = node.parent; + while (parent !== undefined) { + if (predicate(parent)) { + return true; + } + parent = parent.parent; + } + return false; + } + private processParentTypes(parentTypes: ts.NodeArray, typeB: ts.Type): boolean { for (let baseTypeExpr of parentTypes) { let baseType = this.tsTypeChecker.getTypeAtLocation(baseTypeExpr); diff --git a/linter/test/this_type.ts b/linter/test/this_type.ts index ce8847152..15aa9decf 100644 --- a/linter/test/this_type.ts +++ b/linter/test/this_type.ts @@ -22,19 +22,19 @@ interface Adder { } class StringAdder implements Adder { - value = ''; + value = ""; getValue(): string { return this.value; } addFoo(): this { - this.value += 'foo'; + this.value += "foo"; return this; } addBar(): this { - this.value += 'bar'; + this.value += "bar"; return this; } @@ -45,7 +45,7 @@ class StringAdder implements Adder { } const stringAdder: StringAdder = new StringAdder(); -const str = stringAdder.addFoo().addBar().addGreeting('Jane').getValue(); +const str = stringAdder.addFoo().addBar().addGreeting("Jane").getValue(); console.log(str); @@ -53,4 +53,39 @@ class C { m(c: this): void { console.log(c); } -} \ No newline at end of file +} + +class HH { + f = () => { + this; + }; + ff() { + this; + let fff = () => { + this; + }; + fff(); + } + ffff(): void { + this; + } + static fffff() { + this; + } +} + +let g = () => { + this; +}; +let gg = function () { + this; +}; +function ggg() { + this; +} + +class HHH { + static { + this; + } +} diff --git a/linter/test/this_type.ts.relax.json b/linter/test/this_type.ts.relax.json index 3b9cf7c60..1508e5c96 100644 --- a/linter/test/this_type.ts.relax.json +++ b/linter/test/this_type.ts.relax.json @@ -48,6 +48,31 @@ "line": 53, "column": 8, "problem": "ThisType" + }, + { + "line": 72, + "column": 3, + "problem": "FunctionContainsThis" + }, + { + "line": 77, + "column": 9, + "problem": "FunctionContainsThis" + }, + { + "line": 80, + "column": 10, + "problem": "FunctionContainsThis" + }, + { + "line": 83, + "column": 1, + "problem": "FunctionContainsThis" + }, + { + "line": 88, + "column": 3, + "problem": "FunctionContainsThis" } ] } \ No newline at end of file diff --git a/linter/test/this_type.ts.strict.json b/linter/test/this_type.ts.strict.json index 3b9cf7c60..1631a08e6 100644 --- a/linter/test/this_type.ts.strict.json +++ b/linter/test/this_type.ts.strict.json @@ -48,6 +48,36 @@ "line": 53, "column": 8, "problem": "ThisType" + }, + { + "line": 72, + "column": 3, + "problem": "FunctionContainsThis" + }, + { + "line": 77, + "column": 9, + "problem": "FunctionContainsThis" + }, + { + "line": 80, + "column": 10, + "problem": "FunctionExpression" + }, + { + "line": 80, + "column": 10, + "problem": "FunctionContainsThis" + }, + { + "line": 83, + "column": 1, + "problem": "FunctionContainsThis" + }, + { + "line": 88, + "column": 3, + "problem": "FunctionContainsThis" } ] } \ No newline at end of file -- Gitee