diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index 9a0f958dd60d3038f55a62e81d0ad1584875003d..39c76b7fc6f588b9730d31af1d42525e5c21cc21 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -3789,23 +3789,27 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } const methodName = node.name.text; if (allBaseTypes && allBaseTypes.length > 0) { - this.checkMethodType(allBaseTypes, methodName, node); + this.checkMethodType(allBaseTypes, methodName, node, isStatic); } } - private checkMethodType(allBaseTypes: ts.Type[], methodName: string, node: ts.MethodDeclaration): void { + private checkMethodType(allBaseTypes: ts.Type[], methodName: string, node: ts.MethodDeclaration, isStatic: boolean = false): void { for (const baseType of allBaseTypes) { - const baseMethod = baseType.getProperty(methodName); + let baseMethod: ts.Symbol | undefined; + if (isStatic) { + const constructorType = this.tsTypeChecker.getTypeOfSymbolAtLocation(baseType.getSymbol()!, node); + baseMethod = constructorType.getProperty(methodName) || + baseType.getSymbol()?.members?.get(ts.escapeLeadingUnderscores(methodName)); + } else { + baseMethod = baseType.getProperty(methodName); + } if (!baseMethod) { continue; } - - const baseMethodDecl = baseMethod.declarations?.find((d) => { - return ( - (ts.isMethodDeclaration(d) || ts.isMethodSignature(d)) && - this.tsTypeChecker.getTypeAtLocation(d.parent) === baseType - ); - }) as ts.MethodDeclaration | ts.MethodSignature; + const baseMethodDecl = baseMethod.declarations?.find(d => + (ts.isMethodDeclaration(d) || ts.isMethodSignature(d)) && + this.isSameDeclarationType(d.parent, baseType, isStatic) + ) as ts.MethodDeclaration | ts.MethodSignature; if (!baseMethodDecl) { continue; @@ -3819,6 +3823,14 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } } + private isSameDeclarationType(decl: ts.Node, type: ts.Type, isStatic: boolean): boolean { + if (isStatic && ts.isClassDeclaration(decl)) { + const staticType = this.tsTypeChecker.getTypeAtLocation(decl); + return this.isSameType(staticType, type); + } + return this.tsTypeChecker.getTypeAtLocation(decl) === type; + } + private checkIncompatibleFunctionTypes(method: ts.MethodDeclaration): void { const declaredReturnType = this.getActualReturnType(method); if (!declaredReturnType) { @@ -3926,21 +3938,7 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { private getAllBaseTypes(type: ts.Type, classDecl: ts.ClassDeclaration, isStatic?: boolean): ts.Type[] | undefined { if (isStatic) { - const baseTypes: ts.Type[] = []; - if (!classDecl.heritageClauses) { - return baseTypes; - } - for (const clause of classDecl.heritageClauses) { - if (clause.token !== ts.SyntaxKind.ExtendsKeyword) { - continue; - } - for (const typeNode of clause.types) { - const baseType = this.tsTypeChecker.getTypeAtLocation(typeNode); - baseTypes.push(baseType); - } - } - - return baseTypes; + return this.getStaticAllBaseTypes(classDecl); } const baseClasses = type.getBaseTypes() || []; @@ -3952,6 +3950,7 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { if (!classDecl.heritageClauses) { return resolvedBaseClasses; } + const interfaces: ts.Type[] = []; for (const clause of classDecl.heritageClauses) { if (clause.token !== ts.SyntaxKind.ImplementsKeyword) { @@ -3973,6 +3972,32 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return [...resolvedBaseClasses, ...interfaces]; } + private getStaticAllBaseTypes(classDecl: ts.ClassDeclaration): ts.Type[] | undefined { + const baseTypes: ts.Type[] = []; + if (!classDecl.heritageClauses) { + return baseTypes; + } + + for (const clause of classDecl.heritageClauses) { + if (clause.token !== ts.SyntaxKind.ExtendsKeyword) { + continue; + } + + for (const typeNode of clause.types) { + const baseType = this.tsTypeChecker.getTypeAtLocation(typeNode); + baseTypes.push(baseType); + + const baseDecl = baseType.getSymbol()?.declarations?.[0]; + if (baseDecl && ts.isClassDeclaration(baseDecl)) { + const staticBaseType = this.tsTypeChecker.getTypeAtLocation(baseDecl); + const staticBaseTypes = this.getAllBaseTypes(staticBaseType, baseDecl, true) || []; + baseTypes.push(...staticBaseTypes); + } + } + } + return baseTypes; + } + /** * Checks method parameter compatibility * Derived parameter types must be same or wider than base (contravariance principle) diff --git a/ets2panda/linter/test/main/method_inheritance2.ets.arkts2.json b/ets2panda/linter/test/main/method_inheritance2.ets.arkts2.json index 0a8b389c110fd996dd9ff65f6067e69b4f12f3ce..bfb88a65c82c067497faa74d062e2d596c7c90dc 100755 --- a/ets2panda/linter/test/main/method_inheritance2.ets.arkts2.json +++ b/ets2panda/linter/test/main/method_inheritance2.ets.arkts2.json @@ -104,6 +104,16 @@ "rule": "Overridden method parameters and return types must respect type inheritance principles (arkts-method-inherit-rule)", "severity": "ERROR" }, + { + "line": 103, + "column": 18, + "endLine": 103, + "endColumn": 19, + "problem": "MethodInheritRule", + "suggest": "", + "rule": "Overridden method parameters and return types must respect type inheritance principles (arkts-method-inherit-rule)", + "severity": "ERROR" + }, { "line": 108, "column": 17, @@ -113,6 +123,16 @@ "suggest": "", "rule": "Overridden method parameters and return types must respect type inheritance principles (arkts-method-inherit-rule)", "severity": "ERROR" + }, + { + "line": 112, + "column": 18, + "endLine": 112, + "endColumn": 19, + "problem": "MethodInheritRule", + "suggest": "", + "rule": "Overridden method parameters and return types must respect type inheritance principles (arkts-method-inherit-rule)", + "severity": "ERROR" } ] } \ No newline at end of file