From 7c2373bb5f7badaf4edc7efcb960b07e360edddb Mon Sep 17 00:00:00 2001 From: Evgeniy Okolnov Date: Tue, 19 Mar 2024 20:39:03 +0300 Subject: [PATCH 1/3] [ArkTS Linter] Implement `arkts-sendable-class-inheritance` rule. Signed-off-by: Evgeniy Okolnov --- ets2panda/linter/lib/CookBookMsg.ts | 1 + ets2panda/linter/lib/FaultAttrs.ts | 1 + ets2panda/linter/lib/FaultDesc.ts | 1 + ets2panda/linter/lib/Problems.ts | 1 + ets2panda/linter/lib/TypeScriptLinter.ts | 23 ++++--- ets2panda/linter/lib/utils/TsUtils.ts | 62 +++++++++++++++++-- .../linter/test/sendable_class_inheritance.ts | 45 ++++++++++++++ ...sendable_class_inheritance.ts.autofix.json | 55 ++++++++++++++++ .../test/sendable_class_inheritance.ts.json | 50 +++++++++++++++ 9 files changed, 225 insertions(+), 14 deletions(-) create mode 100644 ets2panda/linter/test/sendable_class_inheritance.ts create mode 100644 ets2panda/linter/test/sendable_class_inheritance.ts.autofix.json create mode 100644 ets2panda/linter/test/sendable_class_inheritance.ts.json diff --git a/ets2panda/linter/lib/CookBookMsg.ts b/ets2panda/linter/lib/CookBookMsg.ts index 20763e1d..b76ff8f1 100644 --- a/ets2panda/linter/lib/CookBookMsg.ts +++ b/ets2panda/linter/lib/CookBookMsg.ts @@ -177,3 +177,4 @@ cookBookTag[149] = 'Classes cannot be used as objects (arkts-no-classes-as-obj)' cookBookTag[150] = '"import" statements after other statements are not allowed (arkts-no-misplaced-imports)'; cookBookTag[151] = 'Usage of "ESObject" type is restricted (arkts-limited-esobj)'; cookBookTag[152] = '"Function.apply", "Function.call" are not supported (arkts-no-func-apply-call)'; +cookBookTag[153] = 'The inheritance for "Sendable" classes is limited (arkts-sendable-class-inheritance)'; diff --git a/ets2panda/linter/lib/FaultAttrs.ts b/ets2panda/linter/lib/FaultAttrs.ts index 016d315f..3bdb392b 100644 --- a/ets2panda/linter/lib/FaultAttrs.ts +++ b/ets2panda/linter/lib/FaultAttrs.ts @@ -105,3 +105,4 @@ faultsAttrs[FaultID.ClassAsObject] = new FaultAttributes(149, ProblemSeverity.WA faultsAttrs[FaultID.ImportAfterStatement] = new FaultAttributes(150); faultsAttrs[FaultID.EsObjectType] = new FaultAttributes(151, ProblemSeverity.WARNING); faultsAttrs[FaultID.FunctionApplyCall] = new FaultAttributes(152); +faultsAttrs[FaultID.SendableClassInheritance] = new FaultAttributes(153); diff --git a/ets2panda/linter/lib/FaultDesc.ts b/ets2panda/linter/lib/FaultDesc.ts index b22f575a..073be58d 100644 --- a/ets2panda/linter/lib/FaultDesc.ts +++ b/ets2panda/linter/lib/FaultDesc.ts @@ -97,3 +97,4 @@ faultDesc[FaultID.ErrorSuppression] = 'Error suppression annotation'; faultDesc[FaultID.StrictDiagnostic] = 'Strict diagnostic'; faultDesc[FaultID.ImportAfterStatement] = 'Import declaration after other declaration or statement'; faultDesc[FaultID.EsObjectType] = 'Restricted "ESObject" type'; +faultDesc[FaultID.SendableClassInheritance] = 'Sendable class inheritance'; diff --git a/ets2panda/linter/lib/Problems.ts b/ets2panda/linter/lib/Problems.ts index b8b43fc6..c8f528dd 100644 --- a/ets2panda/linter/lib/Problems.ts +++ b/ets2panda/linter/lib/Problems.ts @@ -94,6 +94,7 @@ export enum FaultID { StrictDiagnostic, ImportAfterStatement, EsObjectType, + SendableClassInheritance, // this should always be last enum LAST_ID } diff --git a/ets2panda/linter/lib/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts index d43f928e..67c5e211 100644 --- a/ets2panda/linter/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -815,13 +815,8 @@ export class TypeScriptLinter { // Filter out non-initializable property decorators from strict diagnostics. if (this.tscStrictDiagnostics && this.sourceFile) { if ( - decorators?.some((x) => { - let decoratorName = ''; - if (ts.isIdentifier(x.expression)) { - decoratorName = x.expression.text; - } else if (ts.isCallExpression(x.expression) && ts.isIdentifier(x.expression.expression)) { - decoratorName = x.expression.expression.text; - } + decorators?.some((decorator) => { + const decoratorName = TsUtils.getDecoratorName(decorator); // special case for property of type CustomDialogController of the @CustomDialog-decorated class if (expectedDecorators.includes(NON_INITIALIZABLE_PROPERTY_CLASS_DECORATORS[0])) { return expectedDecorators.includes(decoratorName) && propType === 'CustomDialogController'; @@ -1323,11 +1318,19 @@ export class TypeScriptLinter { } this.countClassMembersWithDuplicateName(tsClassDecl); + const isSendableClass = TsUtils.hasSendableDecorator(tsClassDecl); const visitHClause = (hClause: ts.HeritageClause): void => { for (const tsTypeExpr of hClause.types) { - const tsExprType = this.tsTypeChecker.getTypeAtLocation(tsTypeExpr.expression); - if (tsExprType.isClass() && hClause.token === ts.SyntaxKind.ImplementsKeyword) { - this.incrementCounters(tsTypeExpr, FaultID.ImplementsClass); + const tsExprType = TsUtils.reduceReference(this.tsTypeChecker.getTypeAtLocation(tsTypeExpr)); + if (tsExprType.isClass()) { + if (hClause.token === ts.SyntaxKind.ImplementsKeyword) { + this.incrementCounters(tsTypeExpr, FaultID.ImplementsClass); + } + + const isSendableBaseType = this.tsUtils.isSendableType(tsExprType); + if (isSendableClass && !isSendableBaseType || !isSendableClass && isSendableBaseType) { + this.incrementCounters(tsTypeExpr, FaultID.SendableClassInheritance); + } } } }; diff --git a/ets2panda/linter/lib/utils/TsUtils.ts b/ets2panda/linter/lib/utils/TsUtils.ts index bb435874..861f80eb 100644 --- a/ets2panda/linter/lib/utils/TsUtils.ts +++ b/ets2panda/linter/lib/utils/TsUtils.ts @@ -234,7 +234,7 @@ export class TsUtils { } // does something similar to relatedByInheritanceOrIdentical function - isOrDerivedFrom(tsType: ts.Type, checkType: CheckType): boolean { + isOrDerivedFrom(tsType: ts.Type, checkType: CheckType, checkedBaseTypes?: Set): boolean { tsType = TsUtils.reduceReference(tsType); if (checkType.call(this, tsType)) { @@ -245,6 +245,9 @@ export class TsUtils { return false; } + // Avoid type recursion in heritage by caching checked types. + (checkedBaseTypes ||= new Set()).add(tsType); + for (const tsTypeDecl of tsType.symbol.declarations) { const isClassOrInterfaceDecl = ts.isClassDeclaration(tsTypeDecl) || ts.isInterfaceDeclaration(tsTypeDecl); const isDerived = isClassOrInterfaceDecl && !!tsTypeDecl.heritageClauses; @@ -252,7 +255,7 @@ export class TsUtils { continue; } for (const heritageClause of tsTypeDecl.heritageClauses) { - if (this.processParentTypesCheck(heritageClause.types, checkType)) { + if (this.processParentTypesCheck(heritageClause.types, checkType, checkedBaseTypes)) { return true; } } @@ -584,10 +587,18 @@ export class TsUtils { return false; } - private processParentTypesCheck(parentTypes: ts.NodeArray, checkType: CheckType): boolean { + private processParentTypesCheck( + parentTypes: ts.NodeArray, + checkType: CheckType, + checkedBaseTypes: Set + ): boolean { for (const baseTypeExpr of parentTypes) { const baseType = TsUtils.reduceReference(this.tsTypeChecker.getTypeAtLocation(baseTypeExpr)); - if (baseType && this.isOrDerivedFrom(baseType, checkType)) { + if ( + baseType && + !checkedBaseTypes.has(baseType) && + this.isOrDerivedFrom(baseType, checkType, checkedBaseTypes) + ) { return true; } } @@ -1672,4 +1683,47 @@ export class TsUtils { return false; }); } + + static getDecoratorName(decorator: ts.Decorator): string { + let decoratorName = ''; + if (ts.isIdentifier(decorator.expression)) { + decoratorName = decorator.expression.text; + } else if (ts.isCallExpression(decorator.expression) && ts.isIdentifier(decorator.expression.expression)) { + decoratorName = decorator.expression.expression.text; + } + return decoratorName; + } + + isSendableType(type: ts.Type): boolean { + const sym = type.getSymbol(); + if (!sym) { + return false; + } + + // class with @Sendable decorator + if (type.isClass()) { + if (sym.declarations?.length) { + const decl = sym.declarations[0]; + if (ts.isClassDeclaration(decl) && TsUtils.hasSendableDecorator(decl)) { + return true; + } + } + } + + // ISendable interface, or a class/interface that implements/extends ISendable interface + return this.isOrDerivedFrom(type, TsUtils.isISendableInterface); + } + + static hasSendableDecorator(decl: ts.ClassDeclaration): boolean { + const decorators = ts.getDecorators(decl); + return decorators !== undefined && decorators.some((x) => { + return TsUtils.getDecoratorName(x) === 'Sendable'; + }); + } + + static isISendableInterface(type: ts.Type): boolean { + const sym = type.getSymbol(); + return sym !== undefined && TsUtils.isObjectType(type) && (type.objectFlags & ts.ObjectFlags.Interface) !== 0 && + sym.name === 'ISendable'; + } } diff --git a/ets2panda/linter/test/sendable_class_inheritance.ts b/ets2panda/linter/test/sendable_class_inheritance.ts new file mode 100644 index 00000000..ef110328 --- /dev/null +++ b/ets2panda/linter/test/sendable_class_inheritance.ts @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 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. + */ + +let Sendable = (x, y) => {} + +// Sendable class inheritance +class NonSendableClass {} + +@Sendable +class SendableClass {} // OK + +@Sendable +class BadInheritance1 extends NonSendableClass {} // ERROR, extends non-sendable + +class BadInheritance2 extends SendableClass {} // ERROR, no @Sendable decorator + +@Sendable +class GoodInheritance extends SendableClass {} // OK + +// Implement ISendable interface +interface ISendable {} + +class SendableImpl implements ISendable {} // OK, class is now sendable + +class BadInterfaceImpl extends SendableImpl {} // ERROR, no @Sendable decorator + +@Sendable +class GoodInterfaceImpl extends SendableImpl {} // OK + +// Implement interface extending ISendable +interface ISendableExt extends ISendable {} + +class GoodInterfaceExtImpl implements ISendableExt {} // OK, class implements interface that extends ISendable diff --git a/ets2panda/linter/test/sendable_class_inheritance.ts.autofix.json b/ets2panda/linter/test/sendable_class_inheritance.ts.autofix.json new file mode 100644 index 00000000..122a08fe --- /dev/null +++ b/ets2panda/linter/test/sendable_class_inheritance.ts.autofix.json @@ -0,0 +1,55 @@ +{ + "copyright": [ + "Copyright (c) 2024 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." + ], + "nodes": [ + { + "line": 16, + "column": 17, + "problem": "AnyType", + "autofixable": false, + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 16, + "column": 20, + "problem": "AnyType", + "autofixable": false, + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 25, + "column": 31, + "problem": "SendableClassInheritance", + "autofixable": false, + "rule": "The inheritance for \"Sendable\" classes is limited (arkts-sendable-class-inheritance)" + }, + { + "line": 27, + "column": 31, + "problem": "SendableClassInheritance", + "autofixable": false, + "rule": "The inheritance for \"Sendable\" classes is limited (arkts-sendable-class-inheritance)" + }, + { + "line": 37, + "column": 32, + "problem": "SendableClassInheritance", + "autofixable": false, + "rule": "The inheritance for \"Sendable\" classes is limited (arkts-sendable-class-inheritance)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_class_inheritance.ts.json b/ets2panda/linter/test/sendable_class_inheritance.ts.json new file mode 100644 index 00000000..524b6b00 --- /dev/null +++ b/ets2panda/linter/test/sendable_class_inheritance.ts.json @@ -0,0 +1,50 @@ +{ + "copyright": [ + "Copyright (c) 2024 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." + ], + "nodes": [ + { + "line": 16, + "column": 17, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 16, + "column": 20, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + }, + { + "line": 25, + "column": 31, + "problem": "SendableClassInheritance", + "rule": "The inheritance for \"Sendable\" classes is limited (arkts-sendable-class-inheritance)" + }, + { + "line": 27, + "column": 31, + "problem": "SendableClassInheritance", + "rule": "The inheritance for \"Sendable\" classes is limited (arkts-sendable-class-inheritance)" + }, + { + "line": 37, + "column": 32, + "problem": "SendableClassInheritance", + "rule": "The inheritance for \"Sendable\" classes is limited (arkts-sendable-class-inheritance)" + } + ] +} \ No newline at end of file -- Gitee From 5df7ae5f9d859577ec52af08888fa2b8187ca4b0 Mon Sep 17 00:00:00 2001 From: mmorozov Date: Thu, 21 Mar 2024 12:40:41 +0300 Subject: [PATCH 2/3] Add rule for sendable assignment Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/I9AJ1D Tests: Tests for new rule were added Signed-off-by: mmorozov --- ets2panda/linter/lib/CookBookMsg.ts | 1 + ets2panda/linter/lib/FaultAttrs.ts | 1 + ets2panda/linter/lib/FaultDesc.ts | 1 + ets2panda/linter/lib/Problems.ts | 1 + ets2panda/linter/lib/TypeScriptLinter.ts | 13 ++++- ets2panda/linter/test_rules/rule155.ts | 31 ++++++++++ .../linter/test_rules/rule155.ts.autofix.skip | 14 +++++ ets2panda/linter/test_rules/rule155.ts.json | 58 +++++++++++++++++++ 8 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 ets2panda/linter/test_rules/rule155.ts create mode 100644 ets2panda/linter/test_rules/rule155.ts.autofix.skip create mode 100644 ets2panda/linter/test_rules/rule155.ts.json diff --git a/ets2panda/linter/lib/CookBookMsg.ts b/ets2panda/linter/lib/CookBookMsg.ts index b76ff8f1..2e46601e 100644 --- a/ets2panda/linter/lib/CookBookMsg.ts +++ b/ets2panda/linter/lib/CookBookMsg.ts @@ -178,3 +178,4 @@ cookBookTag[150] = '"import" statements after other statements are not allowed ( cookBookTag[151] = 'Usage of "ESObject" type is restricted (arkts-limited-esobj)'; cookBookTag[152] = '"Function.apply", "Function.call" are not supported (arkts-no-func-apply-call)'; cookBookTag[153] = 'The inheritance for "Sendable" classes is limited (arkts-sendable-class-inheritance)'; +cookBookTag[155] = 'Definite assignment assertion in not allowed in "Sendable" classes and interfaces (arkts-sendable-definite-assignment)'; diff --git a/ets2panda/linter/lib/FaultAttrs.ts b/ets2panda/linter/lib/FaultAttrs.ts index 3bdb392b..138856ca 100644 --- a/ets2panda/linter/lib/FaultAttrs.ts +++ b/ets2panda/linter/lib/FaultAttrs.ts @@ -106,3 +106,4 @@ faultsAttrs[FaultID.ImportAfterStatement] = new FaultAttributes(150); faultsAttrs[FaultID.EsObjectType] = new FaultAttributes(151, ProblemSeverity.WARNING); faultsAttrs[FaultID.FunctionApplyCall] = new FaultAttributes(152); faultsAttrs[FaultID.SendableClassInheritance] = new FaultAttributes(153); +faultsAttrs[FaultID.SendableDefiniteAssignment] = new FaultAttributes(155); diff --git a/ets2panda/linter/lib/FaultDesc.ts b/ets2panda/linter/lib/FaultDesc.ts index 073be58d..11b05a8a 100644 --- a/ets2panda/linter/lib/FaultDesc.ts +++ b/ets2panda/linter/lib/FaultDesc.ts @@ -98,3 +98,4 @@ faultDesc[FaultID.StrictDiagnostic] = 'Strict diagnostic'; faultDesc[FaultID.ImportAfterStatement] = 'Import declaration after other declaration or statement'; faultDesc[FaultID.EsObjectType] = 'Restricted "ESObject" type'; faultDesc[FaultID.SendableClassInheritance] = 'Sendable class inheritance'; +faultDesc[FaultID.SendableDefiniteAssignment] = 'Use of definite assignment assertin in "Sendable" class of interface'; diff --git a/ets2panda/linter/lib/Problems.ts b/ets2panda/linter/lib/Problems.ts index c8f528dd..5e5e15b7 100644 --- a/ets2panda/linter/lib/Problems.ts +++ b/ets2panda/linter/lib/Problems.ts @@ -95,6 +95,7 @@ export enum FaultID { ImportAfterStatement, EsObjectType, SendableClassInheritance, + SendableDefiniteAssignment, // this should always be last enum LAST_ID } diff --git a/ets2panda/linter/lib/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts index 67c5e211..306e301b 100644 --- a/ets2panda/linter/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -2068,9 +2068,18 @@ export class TypeScriptLinter { } private handleDefiniteAssignmentAssertion(decl: ts.VariableDeclaration | ts.PropertyDeclaration): void { - if (decl.exclamationToken !== undefined) { - this.incrementCounters(decl, FaultID.DefiniteAssignment); + if (decl.exclamationToken === undefined) { + return; + } + + if (decl.kind === ts.SyntaxKind.PropertyDeclaration) { + const parentDecl = decl.parent; + if (parentDecl.kind === ts.SyntaxKind.ClassDeclaration && TsUtils.hasSendableDecorator(parentDecl)) { + this.incrementCounters(decl, FaultID.SendableDefiniteAssignment); + return; + } } + this.incrementCounters(decl, FaultID.DefiniteAssignment); } private readonly validatedTypesSet = new Set(); diff --git a/ets2panda/linter/test_rules/rule155.ts b/ets2panda/linter/test_rules/rule155.ts new file mode 100644 index 00000000..38cfeb1d --- /dev/null +++ b/ets2panda/linter/test_rules/rule155.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022-2024 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. + */ + +@Sendable +class A{ + a: number; + b: number = 1; + c!: number; + d!: number = 1; + e?: number; +} + +class B{ + a: number; + b: number = 1; + c!: number; + d!: number = 1; + e?: number; +} diff --git a/ets2panda/linter/test_rules/rule155.ts.autofix.skip b/ets2panda/linter/test_rules/rule155.ts.autofix.skip new file mode 100644 index 00000000..13982e9c --- /dev/null +++ b/ets2panda/linter/test_rules/rule155.ts.autofix.skip @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2023-2024 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. + */ diff --git a/ets2panda/linter/test_rules/rule155.ts.json b/ets2panda/linter/test_rules/rule155.ts.json new file mode 100644 index 00000000..a19d7994 --- /dev/null +++ b/ets2panda/linter/test_rules/rule155.ts.json @@ -0,0 +1,58 @@ +{ + "copyright": [ + "Copyright (c) 2024 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." + ], + "nodes": [ + { + "line": 20, + "column": 5, + "problem": "SendableDefiniteAssignment", + "rule": "Definite assignment assertion in not allowed in \"Sendable\" classes and interfaces (arkts-sendable-definite-assignment)" + }, + { + "line": 21, + "column": 5, + "problem": "SendableDefiniteAssignment", + "rule": "Definite assignment assertion in not allowed in \"Sendable\" classes and interfaces (arkts-sendable-definite-assignment)" + }, + { + "line": 28, + "column": 5, + "problem": "DefiniteAssignment", + "suggest": "", + "rule": "Definite assignment assertions are not supported (arkts-no-definite-assignment)" + }, + { + "line": 29, + "column": 5, + "problem": "DefiniteAssignment", + "suggest": "", + "rule": "Definite assignment assertions are not supported (arkts-no-definite-assignment)" + }, + { + "line": 18, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Property 'a' has no initializer and is not definitely assigned in the constructor.", + "rule": "Property 'a' has no initializer and is not definitely assigned in the constructor." + }, + { + "line": 26, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Property 'a' has no initializer and is not definitely assigned in the constructor.", + "rule": "Property 'a' has no initializer and is not definitely assigned in the constructor." + } + ] +} -- Gitee From 6514f26797868027413a3fb514a259f56043ce27 Mon Sep 17 00:00:00 2001 From: Igor Rossinski Date: Thu, 28 Mar 2024 03:24:40 +0300 Subject: [PATCH 3/3] [ArkTS Linter] issue #16375 relax rule #17 arkts-no-index-member for .d.ets files Signed-off-by: Igor Rossinski --- ets2panda/linter/lib/TypeScriptLinter.ts | 58 +++++++++++++++++-- .../linter/lib/TypeScriptLinterConfig.ts | 1 - ets2panda/linter/lib/utils/TsUtils.ts | 4 ++ 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/ets2panda/linter/lib/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts index 306e301b..0e977f40 100644 --- a/ets2panda/linter/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -190,7 +190,8 @@ export class TypeScriptLinter { [ts.SyntaxKind.ExpressionWithTypeArguments, this.handleExpressionWithTypeArguments], [ts.SyntaxKind.ComputedPropertyName, this.handleComputedPropertyName], [ts.SyntaxKind.Constructor, this.handleConstructorDeclaration], - [ts.SyntaxKind.PrivateIdentifier, this.handlePrivateIdentifier] + [ts.SyntaxKind.PrivateIdentifier, this.handlePrivateIdentifier], + [ts.SyntaxKind.IndexSignature, this.handleIndexSignature] ]); private getLineAndCharacterOfNode(node: ts.Node | ts.CommentRange): ts.LineAndCharacter { @@ -966,7 +967,7 @@ export class TypeScriptLinter { const isSignature = ts.isMethodSignature(funcLikeDecl); if (isSignature || !funcLikeDecl.body) { // Ambient flag is not exposed, so we apply dirty hack to make it visible - const isAmbientDeclaration = !!(funcLikeDecl.flags & (ts.NodeFlags as any).Ambient); + const isAmbientDeclaration = TsUtils.hasAmbientFlag(funcLikeDecl); //!!(funcLikeDecl.flags & (ts.NodeFlags as any).Ambient); if ((isSignature || isAmbientDeclaration) && !funcLikeDecl.type) { this.incrementCounters(funcLikeDecl, FaultID.LimitedReturnTypeInference); } @@ -1564,10 +1565,32 @@ export class TypeScriptLinter { } } - private isElementAcessAllowed(type: ts.Type): boolean { + + private isValidAmbientDeclArray(type: ts.Type): boolean { + const decl = TsUtils.getDeclaration( type.symbol ); + if ( !! decl && type.isClassOrInterface() ) { + let classDecl = decl as ts.ClassDeclaration; + let classMembers = classDecl.members; + let hasIndexSignature = false; + for( let node of classMembers) { + if( node.kind === ts.SyntaxKind.IndexSignature ) { + const parameter = (node as ts.IndexSignatureDeclaration).parameters[0]; + const paramType = this.tsTypeChecker.getTypeAtLocation( parameter); + if( TsUtils.isNumberLikeType( paramType ) ) + hasIndexSignature = true; + break; + } + } + return decl.getSourceFile().fileName.toLowerCase().endsWith(".d.ets") && + TsUtils.hasAmbientFlag(decl) ; + } + return false; + } + + private isElementAcessAllowed(type: ts.Type, node: ts.Node): boolean { if (type.isUnion()) { for (const t of type.types) { - if (!this.isElementAcessAllowed(t)) { + if (!this.isElementAcessAllowed(t, node)) { return false; } } @@ -1575,6 +1598,14 @@ export class TypeScriptLinter { } const typeNode = this.tsTypeChecker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.None); + const tsElementAccessExpr = node as ts.ElementAccessExpression; + const paramType = this.tsTypeChecker.getTypeAtLocation( tsElementAccessExpr.argumentExpression ); + //let fromValidAmbientDecalaration = TsUtils.isNumberLikeType( paramType ); + //const decl = TsUtils.getDeclaration( type.symbol ); + //const isValidAmbientDeclaration = !! decl && + // decl.getSourceFile().fileName.toLowerCase().endsWith(".d.ets") && + // TsUtils.hasAmbientFlag(decl) ; + return ( this.tsUtils.isLibraryType(type) || @@ -1584,7 +1615,9 @@ export class TypeScriptLinter { this.tsUtils.isOrDerivedFrom(type, this.tsUtils.isStdRecordType) || TsUtils.isEnumType(type) || // we allow EsObject here beacuse it is reported later using FaultId.EsObjectType - TsUtils.isEsObjectType(typeNode) + TsUtils.isEsObjectType(typeNode) || + // allow for ambient declaration with index signature + ( TsUtils.isNumberLikeType( paramType ) && this.tsUtils.isOrDerivedFrom(type, this.isValidAmbientDeclArray ) ) ); } @@ -1599,7 +1632,7 @@ export class TypeScriptLinter { // unnamed types do not have symbol, so need to check that explicitly !this.tsUtils.isLibrarySymbol(tsElementAccessExprSymbol) && !ts.isArrayLiteralExpression(tsElementAccessExpr.expression) && - !this.isElementAcessAllowed(tsElemAccessBaseExprType) + !this.isElementAcessAllowed(tsElemAccessBaseExprType, tsElementAccessExpr) ) { let autofix = Autofixer.fixPropertyAccessByIndex(node); let autofixable = false; @@ -2272,6 +2305,19 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.PrivateIdentifier, autofixable, autofix); } + private handleIndexSignature(node: ts.Node): void { + // relax rule 17 for .d.ets files see issue #16375, #16361 + if(node.getSourceFile().fileName.toLowerCase().endsWith(".d.ets")) { + // index signature should have only one parameter + const parameter = (node as ts.IndexSignatureDeclaration).parameters[0]; + const paramType = this.tsTypeChecker.getTypeAtLocation( parameter); + const classNode = node.parent; + if( TsUtils.isNumberLikeType( paramType ) && TsUtils.hasAmbientFlag(classNode) ) + return; + } + this.incrementCounters(node, FaultID.IndexMember); + } + private createSyncAutofixes(symbol: ts.Symbol, autofixes: Autofix[]): Autofix[] { if (!this.syncAutofixes.has(symbol)) { this.syncAutofixes.set(symbol, []); diff --git a/ets2panda/linter/lib/TypeScriptLinterConfig.ts b/ets2panda/linter/lib/TypeScriptLinterConfig.ts index 0429b16b..da0f91b3 100644 --- a/ets2panda/linter/lib/TypeScriptLinterConfig.ts +++ b/ets2panda/linter/lib/TypeScriptLinterConfig.ts @@ -125,7 +125,6 @@ export class LinterConfig { [ts.SyntaxKind.DeleteExpression, FaultID.DeleteOperator], [ts.SyntaxKind.TypePredicate, FaultID.IsOperator], [ts.SyntaxKind.YieldExpression, FaultID.YieldExpression], - [ts.SyntaxKind.IndexSignature, FaultID.IndexMember], [ts.SyntaxKind.WithStatement, FaultID.WithStatement], [ts.SyntaxKind.IndexedAccessType, FaultID.IndexedAccessType], [ts.SyntaxKind.UnknownKeyword, FaultID.UnknownType], diff --git a/ets2panda/linter/lib/utils/TsUtils.ts b/ets2panda/linter/lib/utils/TsUtils.ts index 861f80eb..870fa87b 100644 --- a/ets2panda/linter/lib/utils/TsUtils.ts +++ b/ets2panda/linter/lib/utils/TsUtils.ts @@ -109,6 +109,10 @@ export class TsUtils { return false; } + static hasAmbientFlag(node: ts.Node): boolean { + return !!(node.flags & (ts.NodeFlags as any).Ambient); + } + static unwrapParenthesized(tsExpr: ts.Expression): ts.Expression { let unwrappedExpr = tsExpr; while (ts.isParenthesizedExpression(unwrappedExpr)) { -- Gitee