diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index 0b7067a939d4e9b2293ad9df1c856b5943891888..7d9a078d5f3eb21e936950d4f57fb5b54f2f84b7 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -8917,41 +8917,57 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { if (!this.options.arkts2 || !this.useStatic) { return; } - - // We are only interested in 'extends' clauses if (node.token !== ts.SyntaxKind.ExtendsKeyword) { return; } - - // Get the parent class declaration (what the child class extends) - const parentClass = this.getParentClassDeclaration(node); - if (!parentClass) { + const expr = node.types[0]?.expression; + if (!expr || !ts.isIdentifier(expr)) { return; } - - // If parent class has a parameterless constructor (or no constructor at all), child is fine - if (TypeScriptLinter.parentHasParameterlessConstructor(parentClass)) { + const symbol = this.tsTypeChecker.getSymbolAtLocation(expr); + if (!symbol) { return; } - - // The child class node (the one extending) const childClass = node.parent; if (!ts.isClassDeclaration(childClass)) { return; } - - // Look for child class constructor - const childConstructor = childClass.members.find(ts.isConstructorDeclaration); - - /* - * If child has no constructor → error (super() cannot be called) - * If child constructor exists but does not contain super() → error - */ - if (!childConstructor?.body || !TypeScriptLinter.childHasSuperCall(childConstructor)) { + const childCtor = childClass.members.find(ts.isConstructorDeclaration); + if (this.isErrorClassWithParamCtor(symbol)) { + this.handleStdLibSuperCall(childCtor, node); + return; + } + const parentClass = this.getParentClassDeclaration(node); + if (!parentClass) { + return; + } + if (TypeScriptLinter.parentHasParameterlessConstructor(parentClass)) { + return; + } + if (!childCtor?.body || !TypeScriptLinter.childHasSuperCall(childCtor)) { + this.incrementCounters(node, FaultID.MissingSuperCall); + } + } + + private handleStdLibSuperCall(childCtor: ts.ConstructorDeclaration | undefined, node: ts.HeritageClause): void { + if (!childCtor || !TypeScriptLinter.childHasSuperCallWithArgs(childCtor)) { this.incrementCounters(node, FaultID.MissingSuperCall); } } + private isErrorClassWithParamCtor(symbol: ts.Symbol): boolean { + return ( + symbol.getName() === 'Error' && + !!symbol.valueDeclaration && + this.tsTypeChecker. + getSignaturesOfType( + this.tsTypeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration), + ts.SignatureKind.Construct + ). + some((sig) => { return sig.getParameters().length > 0; }) + ); + } + /** * Retrieves the parent class declaration node from an extends heritage clause. */ @@ -8997,6 +9013,22 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return superCalled; } + private static childHasSuperCallWithArgs(constructor: ts.ConstructorDeclaration): boolean { + if (!constructor.body) { + return false; + } + for (const stmt of constructor.body.statements) { + if ( + ts.isExpressionStatement(stmt) && + ts.isCallExpression(stmt.expression) && + stmt.expression.expression.kind === ts.SyntaxKind.SuperKeyword + ) { + return stmt.expression.arguments.length > 0; + } + } + return false; + } + private handleInterOpImportJs(importDecl: ts.ImportDeclaration): void { if (!this.options.arkts2 || !importDecl || !this.useStatic) { return; diff --git a/ets2panda/linter/test/main/subclass_super_error_call.ets b/ets2panda/linter/test/main/subclass_super_error_call.ets new file mode 100644 index 0000000000000000000000000000000000000000..6e82a669b5a6056ad7d16bbde423d8d2feda9dab --- /dev/null +++ b/ets2panda/linter/test/main/subclass_super_error_call.ets @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class CustomError1 extends Error {} // ERROR + +class CustomError2 extends Error { // ERROR + constructor(error: Error) { + super(); + } +} + +let err = new CustomError1('foo 42'); + +class UnknownError extends Error{ + error: Error; + constructor(error: Error){ + super(); + this.error = error; + } +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/subclass_super_error_call.ets.args.json b/ets2panda/linter/test/main/subclass_super_error_call.ets.args.json new file mode 100644 index 0000000000000000000000000000000000000000..948b846fe04969bf5ccbe8bd9dc4a18559ce0c2c --- /dev/null +++ b/ets2panda/linter/test/main/subclass_super_error_call.ets.args.json @@ -0,0 +1,19 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "mode": { + "arkts2": "" + } +} diff --git a/ets2panda/linter/test/main/subclass_super_error_call.ets.arkts2.json b/ets2panda/linter/test/main/subclass_super_error_call.ets.arkts2.json new file mode 100644 index 0000000000000000000000000000000000000000..574743bb31fa6a3a60536df48d20113407fdfa2b --- /dev/null +++ b/ets2panda/linter/test/main/subclass_super_error_call.ets.arkts2.json @@ -0,0 +1,68 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [ + { + "line": 16, + "column": 20, + "endLine": 16, + "endColumn": 33, + "problem": "MissingSuperCall", + "suggest": "", + "rule": "The subclass constructor must call the parent class's parametered constructor (arkts-subclass-must-call-super-constructor-with-args)", + "severity": "ERROR" + }, + { + "line": 18, + "column": 20, + "endLine": 18, + "endColumn": 33, + "problem": "MissingSuperCall", + "suggest": "", + "rule": "The subclass constructor must call the parent class's parametered constructor (arkts-subclass-must-call-super-constructor-with-args)", + "severity": "ERROR" + }, + { + "line": 20, + "column": 5, + "endLine": 20, + "endColumn": 10, + "problem": "BuiltinNoCtorFunc", + "suggest": "", + "rule": "API is not support ctor signature and func (arkts-builtin-cotr)", + "severity": "ERROR" + }, + { + "line": 26, + "column": 20, + "endLine": 26, + "endColumn": 33, + "problem": "MissingSuperCall", + "suggest": "", + "rule": "The subclass constructor must call the parent class's parametered constructor (arkts-subclass-must-call-super-constructor-with-args)", + "severity": "ERROR" + }, + { + "line": 29, + "column": 9, + "endLine": 29, + "endColumn": 14, + "problem": "BuiltinNoCtorFunc", + "suggest": "", + "rule": "API is not support ctor signature and func (arkts-builtin-cotr)", + "severity": "ERROR" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/subclass_super_error_call.ets.json b/ets2panda/linter/test/main/subclass_super_error_call.ets.json new file mode 100644 index 0000000000000000000000000000000000000000..ca88f857e960b437dcf767c0ac40be998c8f1236 --- /dev/null +++ b/ets2panda/linter/test/main/subclass_super_error_call.ets.json @@ -0,0 +1,17 @@ +{ + "copyright": [ + "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Licensed under the Apache License, Version 2.0 (the 'License');", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + "http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an 'AS IS' BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + "result": [] +} \ No newline at end of file