diff --git a/ets2panda/linter/rule-config.json b/ets2panda/linter/rule-config.json index 42df3797287831d8d1d132729a4dd602ef436bc3..71a854799d04a3b6dee94f3cacf45a2d2eb6e498 100644 --- a/ets2panda/linter/rule-config.json +++ b/ets2panda/linter/rule-config.json @@ -92,7 +92,8 @@ "arkts-interop-js2s-call-js-method", "arkts-interop-js2s-instanceof-js-type", "arkts-interop-js2s-self-addtion-reduction", - "arkts-promise-with-void-type-need-undefined-as-resolve-arg" + "arkts-promise-with-void-type-need-undefined-as-resolve-arg", + "arkts-class-same-type-prop-with-super" ], "ArkUI": [ "arkui-no-!!-bidirectional-data-binding", @@ -153,4 +154,4 @@ "arkts-limited-stdlib-no-concurrent-decorator", "arkts-no-need-stdlib-worker" ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/src/lib/CookBookMsg.ts b/ets2panda/linter/src/lib/CookBookMsg.ts index e1c85804dd8c93843b2095a13259952d555541d3..3bb45e961528c596e583f81579f11229e885b4a5 100644 --- a/ets2panda/linter/src/lib/CookBookMsg.ts +++ b/ets2panda/linter/src/lib/CookBookMsg.ts @@ -282,6 +282,8 @@ cookBookTag[261] = cookBookTag[262] = 'The makeObserved function is not supported (arkui-no-makeobserved-function)'; cookBookTag[263] = 'The "@Provide" annotation does not support dynamic parameters (arkui-provide-annotation-parameters)'; +cookBookTag[264] = + 'The field types of the subclass and parent class must be the same (arkts-class-same-type-prop-with-super)'; cookBookTag[265] = 'Direct inheritance of interop JS classes is not supported (arkts-interop-js2s-inherit-js-class)'; cookBookTag[266] = 'Direct usage of interop JS objects is not supported (arkts-interop-js2s-traverse-js-instance)'; cookBookTag[267] = 'Direct usage of interop JS functions is not supported (arkts-interop-js2s-js-call-static-function)'; diff --git a/ets2panda/linter/src/lib/FaultAttrs.ts b/ets2panda/linter/src/lib/FaultAttrs.ts index 14bc9b8f4b54ce9c112f6fbe835c7aee3974d213..3bb5dc8a6bb4522e03b9d6f788a6434b608922ff 100644 --- a/ets2panda/linter/src/lib/FaultAttrs.ts +++ b/ets2panda/linter/src/lib/FaultAttrs.ts @@ -187,6 +187,7 @@ faultsAttrs[FaultID.EntryAnnotation] = new FaultAttributes(260); faultsAttrs[FaultID.SdkAbilityLifecycleMonitor] = new FaultAttributes(261); faultsAttrs[FaultID.MakeObservedIsNotSupported] = new FaultAttributes(262); faultsAttrs[FaultID.ProvideAnnotation] = new FaultAttributes(263); +faultsAttrs[FaultID.FieldTypeMismatch] = new FaultAttributes(264); faultsAttrs[FaultID.InteropJsObjectInheritance] = new FaultAttributes(265); faultsAttrs[FaultID.InteropJsObjectTraverseJsInstance] = new FaultAttributes(266); faultsAttrs[FaultID.InteropJsObjectCallStaticFunc] = new FaultAttributes(267, ProblemSeverity.WARNING); diff --git a/ets2panda/linter/src/lib/FaultDesc.ts b/ets2panda/linter/src/lib/FaultDesc.ts index f55dc3b3a1c6518e62946dfd9f1277739f8729a8..41781ddb22afa6ed0152d6533660fad626cbfe9a 100644 --- a/ets2panda/linter/src/lib/FaultDesc.ts +++ b/ets2panda/linter/src/lib/FaultDesc.ts @@ -191,6 +191,7 @@ faultDesc[FaultID.EntryAnnotation] = '"@Entry" decorator parameter'; faultDesc[FaultID.SdkAbilityLifecycleMonitor] = 'UIAbility of 1.2 needs to be listened by the new StaticAbilityLifecycleCallback'; faultDesc[FaultID.ProvideAnnotation] = '"@Provide" decorator parameter'; +faultDesc[FaultID.FieldTypeMismatch] = 'Mismatch field types'; faultDesc[FaultID.InteropJsObjectInheritance] = 'Interop JS class inheritance'; faultDesc[FaultID.InteropJsObjectTraverseJsInstance] = 'Interop JS object traverse usage'; faultDesc[FaultID.InteropJsObjectCallStaticFunc] = 'Interop JS function usage'; diff --git a/ets2panda/linter/src/lib/Problems.ts b/ets2panda/linter/src/lib/Problems.ts index c39e5ac1c3e25636c2b6452cb46e353d006162c8..a221b6e39e18b0a555d1a05423a3595ff2df177f 100644 --- a/ets2panda/linter/src/lib/Problems.ts +++ b/ets2panda/linter/src/lib/Problems.ts @@ -186,6 +186,7 @@ export enum FaultID { EntryAnnotation, SdkAbilityLifecycleMonitor, ProvideAnnotation, + FieldTypeMismatch, UseSharedDeprecated, UseConcurrentDeprecated, MethodInheritRule, diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index 90024f48809f9e480207510eb1298cec7a954b93..bc8354d803604501f91418e72860e8e406ec2f73 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -1188,7 +1188,8 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { if (this.options.arkts2) { const importClause = importDeclNode.importClause; if (!importClause || !importClause.name && !importClause.namedBindings) { - this.incrementCounters(node, FaultID.NoSideEffectImport); + const autofix = this.autofixer?.fixSideEffectImport(importDeclNode); + this.incrementCounters(node, FaultID.NoSideEffectImport, autofix); } else { this.updateDataSdkJsonInfo(importDeclNode, importClause); } @@ -7958,6 +7959,7 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { }); this.handleMissingSuperCallInExtendedClass(node); + this.handleFieldTypesMatchingBetweenDerivedAndBaseClass(node); } } @@ -9757,6 +9759,126 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } } + /** + * Checks that each field in a subclass matches the type of the same-named field + * in its base class. If the subclass field's type is not assignable to the + * base class field type, emit a diagnostic. + */ + private handleFieldTypesMatchingBetweenDerivedAndBaseClass(node: ts.HeritageClause): void { + // Only process "extends" clauses + if (node.token !== ts.SyntaxKind.ExtendsKeyword) { + return; + } + const derivedClass = node.parent; + if (!ts.isClassDeclaration(derivedClass)) { + return; + } + + // Locate the base class declaration + const baseExpr = node.types[0]?.expression; + if (!ts.isIdentifier(baseExpr)) { + return; + } + const baseSym = this.tsUtils.trueSymbolAtLocation(baseExpr); + const baseClassDecl = baseSym?.declarations?.find(ts.isClassDeclaration); + if (!baseClassDecl) { + return; + } + + // Compare each property in the derived class against the base class + for (const member of derivedClass.members) { + if (!ts.isPropertyDeclaration(member) || !ts.isIdentifier(member.name) || !member.type) { + continue; + } + const propName = member.name.text; + // Find the first declaration of this property in the base-class chain + const baseProp = this.findPropertyDeclarationInBaseChain(baseClassDecl, propName); + if (!baseProp) { + continue; + } + + // Get the types + const derivedType = this.tsTypeChecker.getTypeAtLocation(member.type); + const baseType = this.tsTypeChecker.getTypeAtLocation(baseProp.type!); + + // If the derived type is not assignable to the base type, report + if (!this.isFieldTypeMatchingBetweenDerivedAndBaseClass(derivedType, baseType)) { + this.incrementCounters(member.name, FaultID.FieldTypeMismatch); + } + } + } + + /** + * Returns true if the union type members of subclass field's type + * exactly match those of the base class field's type (order-insensitive). + * So `number|string` ↔ `string|number` passes, but `number` ↔ `number|string` fails. + */ + private isFieldTypeMatchingBetweenDerivedAndBaseClass(derivedType: ts.Type, baseType: ts.Type): boolean { + // Split union type strings into trimmed member names + const derivedNames = this.tsTypeChecker. + typeToString(derivedType). + split('|'). + map((s) => { + return s.trim(); + }); + const baseNames = this.tsTypeChecker. + typeToString(baseType). + split('|'). + map((s) => { + return s.trim(); + }); + + // Only match if both unions contain exactly the same members + if (derivedNames.length !== baseNames.length) { + return false; + } + return ( + derivedNames.every((name) => { + return baseNames.includes(name); + }) && + baseNames.every((name) => { + return derivedNames.includes(name); + }) + ); + } + + /** + * Recursively searches base classes to find a property declaration + * with the given name and a type annotation. + */ + private findPropertyDeclarationInBaseChain( + classDecl: ts.ClassDeclaration, + propName: string + ): ts.PropertyDeclaration | undefined { + let current: ts.ClassDeclaration | undefined = classDecl; + while (current) { + // Look for the property in this class + const member = current.members.find((m) => { + return ts.isPropertyDeclaration(m) && ts.isIdentifier(m.name) && m.name.text === propName && !!m.type; + }) as ts.PropertyDeclaration | undefined; + if (member) { + return member; + } + + // Move to the next base class if it exists + const extendsClause = current.heritageClauses?.find((c) => { + return c.token === ts.SyntaxKind.ExtendsKeyword; + }); + if (!extendsClause) { + break; + } + const baseExpr = extendsClause.types[0]?.expression; + if (!ts.isIdentifier(baseExpr)) { + break; + } + + const sym = this.tsUtils.trueSymbolAtLocation(baseExpr); + const decl = sym?.declarations?.find(ts.isClassDeclaration); + current = decl; + } + return undefined; + } + /** * Checks for missing super() call in child classes that extend a parent class * with parameterized constructors. If parent class only has parameterized constructors @@ -11021,23 +11143,33 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { staticProps: Map, instanceProps: Map ): void { - body.forEachChild((node) => { + forEachNodeInSubtree(body, (node) => { if (!ts.isReturnStatement(node) || !node.expression) { return; } + const getPropertyAccess = (expr: ts.Expression): ts.PropertyAccessExpression | undefined => { + if (ts.isPropertyAccessExpression(expr)) { + return expr; + } - const isStaticPropertyAccess = (node: ts.Expression, className: string): boolean => { - return ( - ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === className - ); + if (ts.isCallExpression(expr) && ts.isPropertyAccessExpression(expr.expression)) { + return expr.expression; + } + + return undefined; }; + + const isStaticPropertyAccess = (expr: ts.PropertyAccessExpression, className: string): boolean => { + return ts.isIdentifier(expr.expression) && expr.expression.text === className; + }; + const isInstancePropertyAccess = (node: ts.Expression): boolean => { return ts.isPropertyAccessExpression(node) && node.expression.kind === ts.SyntaxKind.ThisKeyword; }; - if (className && isStaticPropertyAccess(node.expression, className)) { - this.checkPropertyAccess(node, node.expression as ts.PropertyAccessExpression, staticProps, methodReturnType); - return; + const propExp = getPropertyAccess(node.expression); + if (className && propExp && isStaticPropertyAccess(propExp, className)) { + this.checkPropertyAccess(node, propExp, staticProps, methodReturnType); } if (isInstancePropertyAccess(node.expression)) { @@ -11726,12 +11858,26 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return ts.isCallExpression(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node); } + private isConcatArray(accessedNode: ts.Node): boolean { + if (!ts.isIdentifier(accessedNode)) { + return false; + } + const decl = this.tsUtils.getDeclarationNode(accessedNode); + if (!decl) { + return false; + } + + const type = this.tsTypeChecker.getTypeAtLocation(decl); + return TsUtils.isConcatArrayType(type); + } + private getArrayAccessInfo(expr: ts.ElementAccessExpression): false | ArrayAccess { if (!ts.isIdentifier(expr.expression)) { return false; } const baseType = this.tsTypeChecker.getTypeAtLocation(expr.expression); - if (!this.tsUtils.isArray(baseType)) { + + if (!this.tsUtils.isArray(baseType) && !this.isConcatArray(expr.expression)) { return false; } const accessArgument = expr.argumentExpression; diff --git a/ets2panda/linter/src/lib/autofixes/Autofixer.ts b/ets2panda/linter/src/lib/autofixes/Autofixer.ts index dd88357f643f6c820f955aef75d578eac5fdf105..52855144625fada6b2e2aa01d860716ed6663a0d 100644 --- a/ets2panda/linter/src/lib/autofixes/Autofixer.ts +++ b/ets2panda/linter/src/lib/autofixes/Autofixer.ts @@ -5440,4 +5440,24 @@ export class Autofixer { return undefined; } + + fixSideEffectImport(importDeclNode: ts.ImportDeclaration): Autofix[] { + const initModuleCall = ts.factory.createCallExpression( + ts.factory.createIdentifier("initModule"), + undefined, + [importDeclNode.moduleSpecifier] + ); + const expressionStatement = ts.factory.createExpressionStatement(initModuleCall); + const replacedText = this.printer.printNode( + ts.EmitHint.Unspecified, + expressionStatement, + importDeclNode.getSourceFile() + ); + + return [{ + start: importDeclNode.getStart(), + end: importDeclNode.getEnd(), + replacementText: replacedText + }]; + } } diff --git a/ets2panda/linter/src/lib/utils/TsUtils.ts b/ets2panda/linter/src/lib/utils/TsUtils.ts index 917c7e605de2eab2cadb008c34ffde4cbf51f343..32d8ce57719a075450b3bfe10469ae19d9f4ed8e 100644 --- a/ets2panda/linter/src/lib/utils/TsUtils.ts +++ b/ets2panda/linter/src/lib/utils/TsUtils.ts @@ -46,7 +46,7 @@ import { ETS } from './consts/TsSuffix'; import { STRINGLITERAL_NUMBER, STRINGLITERAL_NUMBER_ARRAY } from './consts/StringLiteral'; import { ETS_MODULE, PATH_SEPARATOR, VALID_OHM_COMPONENTS_MODULE_PATH } from './consts/OhmUrl'; import { EXTNAME_ETS, EXTNAME_JS, EXTNAME_D_ETS } from './consts/ExtensionName'; -import { STRING_ERROR_LITERAL } from './consts/Literals'; +import { CONCAT_ARRAY, STRING_ERROR_LITERAL } from './consts/Literals'; export const PROMISE_METHODS = new Set(['all', 'race', 'any', 'resolve', 'allSettled']); export const SYMBOL = 'Symbol'; @@ -334,7 +334,7 @@ export class TsUtils { TsUtils.isTypeReference(tsType) && tsType.typeArguments?.length === 1 && tsType.target.typeParameters?.length === 1 && - tsType.getSymbol()?.getName() === 'ConcatArray' + tsType.getSymbol()?.getName() === CONCAT_ARRAY ); } diff --git a/ets2panda/linter/src/lib/utils/consts/Literals.ts b/ets2panda/linter/src/lib/utils/consts/Literals.ts index 4b543492615e2d214376244cdfcf0408aad1243b..df8054e34348c8645cc37d7dc6a08177220de87c 100644 --- a/ets2panda/linter/src/lib/utils/consts/Literals.ts +++ b/ets2panda/linter/src/lib/utils/consts/Literals.ts @@ -14,3 +14,4 @@ */ export const STRING_ERROR_LITERAL = 'Error'; +export const CONCAT_ARRAY = 'ConcatArray'; diff --git a/ets2panda/linter/test/main/derived_class_field_type_matching.ets b/ets2panda/linter/test/main/derived_class_field_type_matching.ets new file mode 100644 index 0000000000000000000000000000000000000000..2807603e1334df15008dad76a06d1de1bf0fec72 --- /dev/null +++ b/ets2panda/linter/test/main/derived_class_field_type_matching.ets @@ -0,0 +1,61 @@ +/* + * 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 A { + obj1: number | string = 0.0; + obj2: string = 'hello'; + obj3: number | string | boolean = false; +} + +class B extends A { + obj1: string | number = 0.0; // OK + obj2: string = 'hello'; // OK + obj3: number | string | boolean = false; // OK +} + +class C extends A { + obj1: string = 'hello'; // MISMATCH/ERROR + obj2: number = 0.0; // MISMATCH/ERROR + obj3: number | boolean = false; // MISMATCH/ERROR +} + +class D extends A { + obj1: string | number | boolean = 'hello'; // MISMATCH/ERROR + obj2: number | string = 0.0; // MISMATCH/ERROR +} + +class E { obj: number | string = 1.0; } +class F extends E { obj: number = 1.0; } // will be flagged on F vs E => MISMATCH/ERROR +class G extends F { obj: number = 1.0; } // will be checked on G vs F => OK + +class A1 {} +class A2 extends A1 { + obj: number | string = 0.0; +} +class A3 extends A2 {} +class A4 extends A3 { + obj: number = 0.0; // ERROR/MISMATCH => field type is not matching with obj from base class A2 +} + +class B1 { + obj: number | string = 0.0; +} + +class B2 extends B1 { // no error + obj: number | string = 0.0; + obj2: number | string = 0.0; + obj3: boolean = false; + obj4: string = 'hello'; +} diff --git a/ets2panda/linter/test/main/derived_class_field_type_matching.ets.args.json b/ets2panda/linter/test/main/derived_class_field_type_matching.ets.args.json new file mode 100644 index 0000000000000000000000000000000000000000..66fb88f85945924e8be0e83d90123507033f4c5d --- /dev/null +++ b/ets2panda/linter/test/main/derived_class_field_type_matching.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/derived_class_field_type_matching.ets.arkts2.json b/ets2panda/linter/test/main/derived_class_field_type_matching.ets.arkts2.json new file mode 100644 index 0000000000000000000000000000000000000000..e12acd531a0c002f49fca591ed20a024ce5aa4b2 --- /dev/null +++ b/ets2panda/linter/test/main/derived_class_field_type_matching.ets.arkts2.json @@ -0,0 +1,88 @@ +{ + "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": 29, + "column": 3, + "endLine": 29, + "endColumn": 7, + "problem": "FieldTypeMismatch", + "suggest": "", + "rule": "The field types of the subclass and parent class must be the same (arkts-class-same-type-prop-with-super)", + "severity": "ERROR" + }, + { + "line": 30, + "column": 3, + "endLine": 30, + "endColumn": 7, + "problem": "FieldTypeMismatch", + "suggest": "", + "rule": "The field types of the subclass and parent class must be the same (arkts-class-same-type-prop-with-super)", + "severity": "ERROR" + }, + { + "line": 31, + "column": 3, + "endLine": 31, + "endColumn": 7, + "problem": "FieldTypeMismatch", + "suggest": "", + "rule": "The field types of the subclass and parent class must be the same (arkts-class-same-type-prop-with-super)", + "severity": "ERROR" + }, + { + "line": 35, + "column": 3, + "endLine": 35, + "endColumn": 7, + "problem": "FieldTypeMismatch", + "suggest": "", + "rule": "The field types of the subclass and parent class must be the same (arkts-class-same-type-prop-with-super)", + "severity": "ERROR" + }, + { + "line": 36, + "column": 3, + "endLine": 36, + "endColumn": 7, + "problem": "FieldTypeMismatch", + "suggest": "", + "rule": "The field types of the subclass and parent class must be the same (arkts-class-same-type-prop-with-super)", + "severity": "ERROR" + }, + { + "line": 40, + "column": 21, + "endLine": 40, + "endColumn": 24, + "problem": "FieldTypeMismatch", + "suggest": "", + "rule": "The field types of the subclass and parent class must be the same (arkts-class-same-type-prop-with-super)", + "severity": "ERROR" + }, + { + "line": 49, + "column": 3, + "endLine": 49, + "endColumn": 6, + "problem": "FieldTypeMismatch", + "suggest": "", + "rule": "The field types of the subclass and parent class must be the same (arkts-class-same-type-prop-with-super)", + "severity": "ERROR" + } + ] +} diff --git a/ets2panda/linter/test/main/derived_class_field_type_matching.ets.json b/ets2panda/linter/test/main/derived_class_field_type_matching.ets.json new file mode 100644 index 0000000000000000000000000000000000000000..dd03fcf5442488620bcd4b3447f0fcdd89e1905b --- /dev/null +++ b/ets2panda/linter/test/main/derived_class_field_type_matching.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": [] +} diff --git a/ets2panda/linter/test/main/no_side_effect_import.ets.args.json b/ets2panda/linter/test/main/no_side_effect_import.ets.args.json index 4d93062f69db6d74420adeb506e0ca28c5580728..a89d885810708ad03d96e3e14bb6590efd1a7547 100755 --- a/ets2panda/linter/test/main/no_side_effect_import.ets.args.json +++ b/ets2panda/linter/test/main/no_side_effect_import.ets.args.json @@ -14,6 +14,8 @@ "limitations under the License." ], "mode": { - "arkts2": "" + "arkts2": "", + "autofix": "--arkts-2", + "migrate": "--arkts-2" } } diff --git a/ets2panda/linter/test/main/no_side_effect_import.ets.autofix.json b/ets2panda/linter/test/main/no_side_effect_import.ets.autofix.json new file mode 100644 index 0000000000000000000000000000000000000000..4723887320a710cda4191b750032bd80aae8e28a --- /dev/null +++ b/ets2panda/linter/test/main/no_side_effect_import.ets.autofix.json @@ -0,0 +1,110 @@ +{ + "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": 1, + "endLine": 16, + "endColumn": 19, + "problem": "NoSideEffectImport", + "autofix": [ + { + "start": 610, + "end": 628, + "replacementText": "initModule(\"./logger\");", + "line": 16, + "column": 1, + "endLine": 16, + "endColumn": 19 + } + ], + "suggest": "", + "rule": "Import for side-effect only is prohibited.(arkts-no-side-effect-import)", + "severity": "ERROR" + }, + { + "line": 18, + "column": 1, + "endLine": 18, + "endColumn": 23, + "problem": "NoSideEffectImport", + "autofix": [ + { + "start": 669, + "end": 691, + "replacementText": "initModule(\"./utils/init\");", + "line": 18, + "column": 1, + "endLine": 18, + "endColumn": 23 + } + ], + "suggest": "", + "rule": "Import for side-effect only is prohibited.(arkts-no-side-effect-import)", + "severity": "ERROR" + }, + { + "line": 18, + "column": 1, + "endLine": 18, + "endColumn": 23, + "problem": "ImportAfterStatement", + "suggest": "", + "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)", + "severity": "ERROR" + }, + { + "line": 20, + "column": 1, + "endLine": 20, + "endColumn": 45, + "problem": "ImportAfterStatement", + "suggest": "", + "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)", + "severity": "ERROR" + }, + { + "line": 21, + "column": 1, + "endLine": 21, + "endColumn": 31, + "problem": "ImportAfterStatement", + "suggest": "", + "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)", + "severity": "ERROR" + }, + { + "line": 22, + "column": 1, + "endLine": 22, + "endColumn": 34, + "problem": "ImportAfterStatement", + "suggest": "", + "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 1, + "endLine": 24, + "endColumn": 40, + "problem": "ImportAfterStatement", + "suggest": "", + "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)", + "severity": "ERROR" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/no_side_effect_import.ets.migrate.ets b/ets2panda/linter/test/main/no_side_effect_import.ets.migrate.ets new file mode 100644 index 0000000000000000000000000000000000000000..a70297d4caea562168c338d773124639dca8b6ce --- /dev/null +++ b/ets2panda/linter/test/main/no_side_effect_import.ets.migrate.ets @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024-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. + */ + +initModule("./logger"); +console.log("Main program running..."); +initModule("./utils/init"); + +import { cookBookTag } from './CookBookMsg'; +import Logger from './logger'; +import * as Utils from './utils'; + +import { initApp } from './utils/init'; + + diff --git a/ets2panda/linter/test/main/no_side_effect_import.ets.migrate.json b/ets2panda/linter/test/main/no_side_effect_import.ets.migrate.json new file mode 100644 index 0000000000000000000000000000000000000000..e6817505079c74e6aa9b918b489be9036ce13b40 --- /dev/null +++ b/ets2panda/linter/test/main/no_side_effect_import.ets.migrate.json @@ -0,0 +1,58 @@ +{ + "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": 20, + "column": 1, + "endLine": 20, + "endColumn": 45, + "problem": "ImportAfterStatement", + "suggest": "", + "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)", + "severity": "ERROR" + }, + { + "line": 21, + "column": 1, + "endLine": 21, + "endColumn": 31, + "problem": "ImportAfterStatement", + "suggest": "", + "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)", + "severity": "ERROR" + }, + { + "line": 22, + "column": 1, + "endLine": 22, + "endColumn": 34, + "problem": "ImportAfterStatement", + "suggest": "", + "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)", + "severity": "ERROR" + }, + { + "line": 24, + "column": 1, + "endLine": 24, + "endColumn": 40, + "problem": "ImportAfterStatement", + "suggest": "", + "rule": "\"import\" statements after other statements are not allowed (arkts-no-misplaced-imports)", + "severity": "ERROR" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/main/no_ts_like_smart_type.ets b/ets2panda/linter/test/main/no_ts_like_smart_type.ets index 9c49b8c43ccffc82c7ff80e5ccce4d0bcceb18a6..7cdff3d524a3211820c8e0ecad1fe4f5692292f3 100755 --- a/ets2panda/linter/test/main/no_ts_like_smart_type.ets +++ b/ets2panda/linter/test/main/no_ts_like_smart_type.ets @@ -168,4 +168,29 @@ function sleep(ms: number): PromiseLike { return new Promise( (resolve: (value: T | PromiseLike) => void): number => setTimeout(resolve, ms) ); +} + +type TypeA = (key: string) => string | undefined + +class ClassA { + static sA?: TypeA = undefined + + static funA(key: string): string | undefined { + if (ClassA.sA) { + return ClassA.sA(key) //error + } + else { + return undefined; + } + } +} + +class A { + b?: boolean + + isRelease(): boolean { + if (this.b != undefined) { + return this.b //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 1141f2277a9c5b966e5f0f188985bfefd7e3f51c..b46fb92edd1fdc5c1e122aaea725287007ad7e22 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 @@ -354,6 +354,26 @@ "rule": "Type inference in case of generic function calls is limited (arkts-no-inferred-generic-params)", "severity": "ERROR" }, + { + "line": 180, + "column": 13, + "endLine": 180, + "endColumn": 34, + "problem": "NoTsLikeSmartType", + "suggest": "", + "rule": "Smart type differences (arkts-no-ts-like-smart-type)", + "severity": "ERROR" + }, + { + "line": 193, + "column": 13, + "endLine": 193, + "endColumn": 26, + "problem": "NoTsLikeSmartType", + "suggest": "", + "rule": "Smart type differences (arkts-no-ts-like-smart-type)", + "severity": "ERROR" + }, { "line": 137, "column": 5, @@ -383,6 +403,16 @@ "suggest": "'b.a' is possibly 'undefined'.", "rule": "'b.a' is possibly 'undefined'.", "severity": "ERROR" + }, + { + "line": 191, + "column": 18, + "endLine": 191, + "endColumn": 25, + "problem": "StrictDiagnostic", + "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", + "rule": "Function lacks ending return statement and return type does not include 'undefined'.", + "severity": "ERROR" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/main/no_ts_like_smart_type.ets.json b/ets2panda/linter/test/main/no_ts_like_smart_type.ets.json index fd7351eff41090b5a7ad6ffde13531123acab287..0eff9f6a2478414fa918796ff6e19f694e43d858 100755 --- a/ets2panda/linter/test/main/no_ts_like_smart_type.ets.json +++ b/ets2panda/linter/test/main/no_ts_like_smart_type.ets.json @@ -53,6 +53,16 @@ "suggest": "'b.a' is possibly 'undefined'.", "rule": "'b.a' is possibly 'undefined'.", "severity": "ERROR" + }, + { + "line": 191, + "column": 18, + "endLine": 191, + "endColumn": 25, + "problem": "StrictDiagnostic", + "suggest": "Function lacks ending return statement and return type does not include 'undefined'.", + "rule": "Function lacks ending return statement and return type does not include 'undefined'.", + "severity": "ERROR" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/main/runtime_array_bound.ets b/ets2panda/linter/test/main/runtime_array_bound.ets index 934e9df563e128fa24f570286ac41e09eeb61c92..33c5e11fbfee39878779b70ecd8f61efa7ef0471 100644 --- a/ets2panda/linter/test/main/runtime_array_bound.ets +++ b/ets2panda/linter/test/main/runtime_array_bound.ets @@ -295,3 +295,16 @@ let values: string[] = Object.values(arr); for (let i = 0; i < keys.length; i++) { values[i]; } + +let concatArray: ConcatArray = new Array(1.0, 2.0, 3.0); + +let tempNum: number = concatArray[5]; + + +for (let i = 0; i < concatArray.length; i++) { + concatArray[i] +}; + +if (concatArray.length > 10) { + concatArray[10] +}; diff --git a/ets2panda/linter/test/main/runtime_array_bound.ets.args.json b/ets2panda/linter/test/main/runtime_array_bound.ets.args.json index 8a4be28991e8164c0366b8b3ad0dffbc04910a27..18566d7d5af12119eda3f7c6a7d4859530d7b726 100644 --- a/ets2panda/linter/test/main/runtime_array_bound.ets.args.json +++ b/ets2panda/linter/test/main/runtime_array_bound.ets.args.json @@ -1,20 +1,20 @@ { - "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": "", - "migrate": "--arkts-2" - } + "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": "", + "migrate": "--arkts-2" + } } diff --git a/ets2panda/linter/test/main/runtime_array_bound.ets.arkts2.json b/ets2panda/linter/test/main/runtime_array_bound.ets.arkts2.json index ac6388b655d916b890b248e5f5950ce90459433a..eeab471943dea119a5df2506dfe1e2b6cefb3855 100644 --- a/ets2panda/linter/test/main/runtime_array_bound.ets.arkts2.json +++ b/ets2panda/linter/test/main/runtime_array_bound.ets.arkts2.json @@ -2513,6 +2513,46 @@ "suggest": "", "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", "severity": "ERROR" + }, + { + "line": 301, + "column": 23, + "endLine": 301, + "endColumn": 37, + "problem": "RuntimeArrayCheck", + "suggest": "", + "rule": "Array bound not checked. (arkts-runtime-array-check)", + "severity": "ERROR" + }, + { + "line": 304, + "column": 10, + "endLine": 304, + "endColumn": 15, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 304, + "column": 14, + "endLine": 304, + "endColumn": 15, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 308, + "column": 26, + "endLine": 308, + "endColumn": 28, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/runtime_array_bound.ets.json b/ets2panda/linter/test/main/runtime_array_bound.ets.json index bd1b7140ca95b67336908f5fca6dec77fb46b2c0..9f305c86d7ff705098b1e480818e125d5e6e3a4a 100644 --- a/ets2panda/linter/test/main/runtime_array_bound.ets.json +++ b/ets2panda/linter/test/main/runtime_array_bound.ets.json @@ -1,17 +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." - ], + "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": [] } diff --git a/ets2panda/linter/test/main/runtime_array_bound.ets.migrate.ets b/ets2panda/linter/test/main/runtime_array_bound.ets.migrate.ets index 31f0749e9f76421209911521934f7b62d52d0ee0..5531ae7190fbacfb0cd23ea1d5aefaab27680da0 100644 --- a/ets2panda/linter/test/main/runtime_array_bound.ets.migrate.ets +++ b/ets2panda/linter/test/main/runtime_array_bound.ets.migrate.ets @@ -295,3 +295,16 @@ let values: string[] = Object.values(arr); for (let i: number = 0.0; i < keys.length; i++) { values[i as int]; } + +let concatArray: ConcatArray = new Array(1.0, 2.0, 3.0); + +let tempNum: number = concatArray[5]; + + +for (let i: number = 0.0; i < concatArray.length; i++) { + concatArray[i as int] +}; + +if (concatArray.length > 10.0) { + concatArray[10] +}; diff --git a/ets2panda/linter/test/main/runtime_array_bound.ets.migrate.json b/ets2panda/linter/test/main/runtime_array_bound.ets.migrate.json index 3d9a0f6ccc36d3b41c0f957b1f0d03fb2118363d..84420569c7afc81e1a53ff83f2da02c069d5981a 100644 --- a/ets2panda/linter/test/main/runtime_array_bound.ets.migrate.json +++ b/ets2panda/linter/test/main/runtime_array_bound.ets.migrate.json @@ -233,6 +233,16 @@ "suggest": "", "rule": "Array type is immutable in ArkTS1.2 (arkts-array-type-immutable)", "severity": "ERROR" + }, + { + "line": 301, + "column": 23, + "endLine": 301, + "endColumn": 37, + "problem": "RuntimeArrayCheck", + "suggest": "", + "rule": "Array bound not checked. (arkts-runtime-array-check)", + "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/stdlib_array.ets.arkts2.json b/ets2panda/linter/test/main/stdlib_array.ets.arkts2.json index e1640cf89ef3344d347b3562f8fdaf8f3cf37c9e..8e3ee4d132e85b85e33063b7fb3e8042329864ba 100644 --- a/ets2panda/linter/test/main/stdlib_array.ets.arkts2.json +++ b/ets2panda/linter/test/main/stdlib_array.ets.arkts2.json @@ -324,6 +324,16 @@ "rule": "Array bound not checked. (arkts-runtime-array-check)", "severity": "ERROR" }, + { + "line": 49, + "column": 26, + "endLine": 49, + "endColumn": 30, + "problem": "RuntimeArrayCheck", + "suggest": "", + "rule": "Array bound not checked. (arkts-runtime-array-check)", + "severity": "ERROR" + }, { "line": 53, "column": 10, @@ -385,4 +395,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +}