From 6d716cf0660d471842bd998b85337c6436f73dba Mon Sep 17 00:00:00 2001 From: Okolnov Evgeniy Date: Sat, 12 Jul 2025 00:01:09 +0300 Subject: [PATCH] Fix error report for incompatible functions Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICLSOF Test scenarios: new tests added to the linter Signed-off-by: Okolnov Evgeniy --- ets2panda/linter/src/lib/TypeScriptLinter.ts | 8 +- ets2panda/linter/src/lib/utils/TsUtils.ts | 110 +++++------------- .../func_inferred_type_args_2.ets.arkts2.json | 10 ++ ...func_inferred_type_args_2.ets.autofix.json | 10 ++ ...func_inferred_type_args_2.ets.migrate.json | 10 ++ .../test/main/incompatible_function.ets | 21 ++++ .../incompatible_function.ets.arkts2.json | 100 ++++++++++++++++ .../no_ts_like_smart_type.ets.arkts2.json | 10 ++ ...tructural_identity_promise.ets.arkts2.json | 13 ++- 9 files changed, 203 insertions(+), 89 deletions(-) diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index 666921f942..9ef7e38fb6 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -1590,7 +1590,6 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { ); if (node.type && node.initializer) { this.checkAssignmentMatching(node, this.tsTypeChecker.getTypeAtLocation(node.type), node.initializer, true); - this.checkFunctionTypeCompatible(node.type, node.initializer); } this.handleDeclarationInferredType(node); this.handleDefiniteAssignmentAssertion(node); @@ -2124,7 +2123,6 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { this.checkUsageOfTsTypes(leftOperandType, tsBinaryExpr); }); this.checkAssignmentMatching(tsBinaryExpr, leftOperandType, tsRhsExpr); - this.checkFunctionTypeCompatible(typeNode, tsRhsExpr); this.handleEsObjectAssignment(tsBinaryExpr, typeNode, tsRhsExpr); this.handleSdkGlobalApi(tsBinaryExpr); break; @@ -2588,7 +2586,6 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { this.tsTypeChecker.getTypeAtLocation(tsVarDecl.type), tsVarDecl.initializer ); - this.checkFunctionTypeCompatible(tsVarDecl.type, tsVarDecl.initializer); } this.handleEsValueDeclaration(tsVarDecl); this.handleDeclarationInferredType(tsVarDecl); @@ -6726,6 +6723,7 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return; } this.handleStructuralTyping(contextNode, lhsType, rhsType, rhsExpr, isStrict); + this.checkFunctionalTypeCompatibility(lhsType, rhsType, rhsExpr); } private handleStructuralTyping( @@ -7601,8 +7599,8 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { this.incrementCounters(node, FaultID.TaggedTemplates); } - private checkFunctionTypeCompatible(lhsTypeNode: ts.TypeNode | undefined, rhsExpr: ts.Expression): void { - if (this.options.arkts2 && lhsTypeNode && this.tsUtils.isIncompatibleFunctionals(lhsTypeNode, rhsExpr)) { + private checkFunctionalTypeCompatibility(lhsType: ts.Type, rhsType: ts.Type, rhsExpr: ts.Expression): void { + if (this.options.arkts2 && !this.tsUtils.areCompatibleFunctionalTypes(lhsType, rhsType)) { this.incrementCounters(rhsExpr, FaultID.IncompationbleFunctionType); } } diff --git a/ets2panda/linter/src/lib/utils/TsUtils.ts b/ets2panda/linter/src/lib/utils/TsUtils.ts index 7625adfd50..5b3fabffbf 100644 --- a/ets2panda/linter/src/lib/utils/TsUtils.ts +++ b/ets2panda/linter/src/lib/utils/TsUtils.ts @@ -787,7 +787,7 @@ export class TsUtils { return false; } // #14569: Check for Function type. - if (this.areCompatibleFunctionals(lhsType, rhsType)) { + if (this.skipStructuralTypingCheckForFunctionals(lhsType, rhsType)) { return false; } if (rhsType.isUnion() || lhsType.isUnion()) { @@ -2035,115 +2035,59 @@ export class TsUtils { return type; } - private areCompatibleFunctionals(lhsType: ts.Type, rhsType: ts.Type): boolean { + private skipStructuralTypingCheckForFunctionals(lhsType: ts.Type, rhsType: ts.Type): boolean { return ( (this.isStdFunctionType(lhsType) || TsUtils.isFunctionalType(lhsType)) && (this.isStdFunctionType(rhsType) || TsUtils.isFunctionalType(rhsType)) ); } - isIncompatibleFunctionals(lhsTypeNode: ts.TypeNode, rhsExpr: ts.Expression): boolean { - if (ts.isUnionTypeNode(lhsTypeNode)) { - for (let i = 0; i < lhsTypeNode.types.length; i++) { - if (!this.isIncompatibleFunctional(lhsTypeNode.types[i], rhsExpr)) { - return false; + areCompatibleFunctionalTypes(lhsType: ts.Type, rhsType: ts.Type): boolean { + if (lhsType.isUnion()) { + for (let i = 0; i < lhsType.types.length; i++) { + if (this.areCompatibleFunctionalTypes(lhsType.types[i], rhsType)) { + return true; } } - return true; + return false; } - return this.isIncompatibleFunctional(lhsTypeNode, rhsExpr); - } - private isIncompatibleFunctional(lhsTypeNode: ts.TypeNode, rhsExpr: ts.Expression): boolean { - const lhsType = this.tsTypeChecker.getTypeAtLocation(lhsTypeNode); - const rhsType = this.tsTypeChecker.getTypeAtLocation(rhsExpr); - const lhsParams = this.getLhsFunctionParameters(lhsTypeNode); - const rhsParams = this.getRhsFunctionParameters(rhsExpr); + const lhsSignature = TsUtils.getFunctionalTypeSignature(lhsType); + const rhsSignature = TsUtils.getFunctionalTypeSignature(rhsType); + if (!lhsSignature || !rhsSignature) { + return true; + } - if (lhsParams !== rhsParams) { + if (lhsSignature.parameters.length < rhsSignature.parameters.length) { return false; } - if (TsUtils.isFunctionalType(lhsType)) { - const lhsFunctionReturnType = this.getFunctionType(lhsTypeNode); - const rhsReturnType = this.getReturnTypeFromExpression(rhsType); - if (lhsFunctionReturnType && rhsReturnType) { - return TsUtils.isVoidType(lhsFunctionReturnType) && !TsUtils.isVoidType(rhsReturnType); - } + const lhsReturnType = lhsSignature.getReturnType(); + const rhsReturnType = rhsSignature.getReturnType(); + if (lhsReturnType && rhsReturnType) { + return !(TsUtils.isVoidType(lhsReturnType) && !TsUtils.isVoidType(rhsReturnType)); } - return false; + + return true; } static isVoidType(tsType: ts.Type): boolean { return (tsType.getFlags() & ts.TypeFlags.Void) !== 0; } - private getRhsFunctionParameters(expr: ts.Expression): number { - const type = this.tsTypeChecker.getTypeAtLocation(expr); - const signatures = this.tsTypeChecker.getSignaturesOfType(type, ts.SignatureKind.Call); - if (signatures.length > 0) { - const signature = signatures[0]; - return signature.parameters.length; - } - return 0; - } - - private getLhsFunctionParameters(typeNode: ts.TypeNode): number { - let current: ts.TypeNode = typeNode; - while (ts.isTypeReferenceNode(current)) { - const symbol = this.tsTypeChecker.getSymbolAtLocation(current.typeName); - if (!symbol) { - break; - } - - const declaration = symbol.declarations?.[0]; - if (!declaration || !ts.isTypeAliasDeclaration(declaration)) { - break; - } - - current = declaration.type; - } - if (ts.isFunctionTypeNode(current)) { - return current.parameters.length; - } - return 0; - } - - private getFunctionType(typeNode: ts.TypeNode): ts.Type | undefined { - let current: ts.TypeNode = typeNode; - while (ts.isTypeReferenceNode(current)) { - const symbol = this.tsTypeChecker.getSymbolAtLocation(current.typeName); - if (!symbol) { - break; - } - - const declaration = symbol.declarations?.[0]; - if (!declaration || !ts.isTypeAliasDeclaration(declaration)) { - break; - } - - current = declaration.type; - } - if (ts.isFunctionTypeNode(current)) { - return this.tsTypeChecker.getTypeAtLocation(current.type); - } - return undefined; + static isFunctionalType(type: ts.Type): boolean { + const callSigns = type.getCallSignatures(); + return callSigns && callSigns.length > 0; } - private getReturnTypeFromExpression(type: ts.Type): ts.Type | undefined { - const signatures = this.tsTypeChecker.getSignaturesOfType(type, ts.SignatureKind.Call); - if (signatures.length > 0) { - const returnType = this.tsTypeChecker.getReturnTypeOfSignature(signatures[0]); - return returnType; + static getFunctionalTypeSignature(type: ts.Type): ts.Signature | undefined { + const callSigns = type.getCallSignatures(); + if (callSigns.length > 0) { + return callSigns[0]; } return undefined; } - static isFunctionalType(type: ts.Type): boolean { - const callSigns = type.getCallSignatures(); - return callSigns && callSigns.length > 0; - } - static getFunctionReturnType(type: ts.Type): ts.Type | null { const signatures = type.getCallSignatures(); if (signatures.length === 0) { diff --git a/ets2panda/linter/test/main/func_inferred_type_args_2.ets.arkts2.json b/ets2panda/linter/test/main/func_inferred_type_args_2.ets.arkts2.json index d1ca60d4ff..b48bb23715 100644 --- a/ets2panda/linter/test/main/func_inferred_type_args_2.ets.arkts2.json +++ b/ets2panda/linter/test/main/func_inferred_type_args_2.ets.arkts2.json @@ -244,6 +244,16 @@ "rule": "Type inference in case of generic function calls is limited (arkts-no-inferred-generic-params)", "severity": "ERROR" }, + { + "line": 41, + "column": 46, + "endLine": 41, + "endColumn": 66, + "problem": "IncompationbleFunctionType", + "suggest": "", + "rule": "Stricter assignments into variables of function type (arkts-incompatible-function-types)", + "severity": "ERROR" + }, { "line": 41, "column": 34, diff --git a/ets2panda/linter/test/main/func_inferred_type_args_2.ets.autofix.json b/ets2panda/linter/test/main/func_inferred_type_args_2.ets.autofix.json index c53b7da247..8c3c8bc2cf 100644 --- a/ets2panda/linter/test/main/func_inferred_type_args_2.ets.autofix.json +++ b/ets2panda/linter/test/main/func_inferred_type_args_2.ets.autofix.json @@ -354,6 +354,16 @@ "rule": "Type inference in case of generic function calls is limited (arkts-no-inferred-generic-params)", "severity": "ERROR" }, + { + "line": 41, + "column": 46, + "endLine": 41, + "endColumn": 66, + "problem": "IncompationbleFunctionType", + "suggest": "", + "rule": "Stricter assignments into variables of function type (arkts-incompatible-function-types)", + "severity": "ERROR" + }, { "line": 41, "column": 34, diff --git a/ets2panda/linter/test/main/func_inferred_type_args_2.ets.migrate.json b/ets2panda/linter/test/main/func_inferred_type_args_2.ets.migrate.json index 0b10990af1..452d9d6225 100644 --- a/ets2panda/linter/test/main/func_inferred_type_args_2.ets.migrate.json +++ b/ets2panda/linter/test/main/func_inferred_type_args_2.ets.migrate.json @@ -144,6 +144,16 @@ "rule": "Type inference in case of generic function calls is limited (arkts-no-inferred-generic-params)", "severity": "ERROR" }, + { + "line": 48, + "column": 54, + "endLine": 48, + "endColumn": 74, + "problem": "IncompationbleFunctionType", + "suggest": "", + "rule": "Stricter assignments into variables of function type (arkts-incompatible-function-types)", + "severity": "ERROR" + }, { "line": 59, "column": 10, diff --git a/ets2panda/linter/test/main/incompatible_function.ets b/ets2panda/linter/test/main/incompatible_function.ets index 687a6bb89a..db3f4250b7 100644 --- a/ets2panda/linter/test/main/incompatible_function.ets +++ b/ets2panda/linter/test/main/incompatible_function.ets @@ -91,4 +91,25 @@ class B { test(): C { return this.arr; } +} + +function sleep(time: number): Promise { + return new Promise((resolve) => setTimeout(resolve, time)); +} +sleep(100); + +const caseName = 'PromiseAnyTest0200'; +console.info(`${caseName} test start`); +const startTime = Date.now(); +const promises: Promise[] = []; +const totalPromises = 1000; +const delay = 100; +for (let i = 0; i < totalPromises; i++) { + promises.push( + new Promise((_, reject) => + setTimeout(() => { + reject(new Error(`Failure ${i}`)); + }, delay) + ) + ); } \ No newline at end of file diff --git a/ets2panda/linter/test/main/incompatible_function.ets.arkts2.json b/ets2panda/linter/test/main/incompatible_function.ets.arkts2.json index 83f24a944f..d965248483 100644 --- a/ets2panda/linter/test/main/incompatible_function.ets.arkts2.json +++ b/ets2panda/linter/test/main/incompatible_function.ets.arkts2.json @@ -213,6 +213,106 @@ "suggest": "", "rule": "Structural typing is not supported (arkts-no-structural-typing)", "severity": "ERROR" + }, + { + "line": 97, + "column": 31, + "endLine": 97, + "endColumn": 38, + "problem": "IncompationbleFunctionType", + "suggest": "", + "rule": "Stricter assignments into variables of function type (arkts-incompatible-function-types)", + "severity": "ERROR" + }, + { + "line": 99, + "column": 7, + "endLine": 99, + "endColumn": 10, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 103, + "column": 7, + "endLine": 103, + "endColumn": 29, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 105, + "column": 7, + "endLine": 105, + "endColumn": 27, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 105, + "column": 23, + "endLine": 105, + "endColumn": 27, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 106, + "column": 7, + "endLine": 106, + "endColumn": 18, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 106, + "column": 15, + "endLine": 106, + "endColumn": 18, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 107, + "column": 10, + "endLine": 107, + "endColumn": 15, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 107, + "column": 14, + "endLine": 107, + "endColumn": 15, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 109, + "column": 26, + "endLine": 109, + "endColumn": 35, + "problem": "IncompationbleFunctionType", + "suggest": "", + "rule": "Stricter assignments into variables of function type (arkts-incompatible-function-types)", + "severity": "ERROR" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/main/no_ts_like_smart_type.ets.arkts2.json b/ets2panda/linter/test/main/no_ts_like_smart_type.ets.arkts2.json index 2c467f12f3..1141f2277a 100755 --- a/ets2panda/linter/test/main/no_ts_like_smart_type.ets.arkts2.json +++ b/ets2panda/linter/test/main/no_ts_like_smart_type.ets.arkts2.json @@ -334,6 +334,16 @@ "rule": "Smart type differences (arkts-no-ts-like-smart-type)", "severity": "ERROR" }, + { + "line": 169, + "column": 6, + "endLine": 169, + "endColumn": 47, + "problem": "IncompationbleFunctionType", + "suggest": "", + "rule": "Stricter assignments into variables of function type (arkts-incompatible-function-types)", + "severity": "ERROR" + }, { "line": 168, "column": 10, diff --git a/ets2panda/linter/test/main/structural_identity_promise.ets.arkts2.json b/ets2panda/linter/test/main/structural_identity_promise.ets.arkts2.json index ca88f857e9..8687ce3be3 100644 --- a/ets2panda/linter/test/main/structural_identity_promise.ets.arkts2.json +++ b/ets2panda/linter/test/main/structural_identity_promise.ets.arkts2.json @@ -13,5 +13,16 @@ "See the License for the specific language governing permissions and", "limitations under the License." ], - "result": [] + "result": [ + { + "line": 39, + "column": 6, + "endLine": 39, + "endColumn": 50, + "problem": "IncompationbleFunctionType", + "suggest": "", + "rule": "Stricter assignments into variables of function type (arkts-incompatible-function-types)", + "severity": "ERROR" + } + ] } \ No newline at end of file -- Gitee