From 7c2373bb5f7badaf4edc7efcb960b07e360edddb Mon Sep 17 00:00:00 2001 From: Evgeniy Okolnov Date: Tue, 19 Mar 2024 20:39:03 +0300 Subject: [PATCH 1/4] [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/4] 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 0a4282651ec09224888e1e2899aa1c366eb61663 Mon Sep 17 00:00:00 2001 From: Igor Rossinski Date: Mon, 25 Mar 2024 13:29:46 +0300 Subject: [PATCH 3/4] [ArkTS Linter] fix #16276 - captured variables in sendable class Signed-off-by: Igor Rossinski --- ets2panda/linter/lib/CookBookMsg.ts | 6 ++- ets2panda/linter/lib/FaultAttrs.ts | 1 + ets2panda/linter/lib/FaultDesc.ts | 1 + ets2panda/linter/lib/Problems.ts | 1 + ets2panda/linter/lib/TypeScriptLinter.ts | 39 ++++++++++++++++ .../test/sendable_captured_variables.ts | 35 +++++++++++++++ ...endable_captured_variables.ts.autofix.json | 44 +++++++++++++++++++ .../test/sendable_captured_variables.ts.json | 39 ++++++++++++++++ 8 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 ets2panda/linter/test/sendable_captured_variables.ts create mode 100644 ets2panda/linter/test/sendable_captured_variables.ts.autofix.json create mode 100644 ets2panda/linter/test/sendable_captured_variables.ts.json diff --git a/ets2panda/linter/lib/CookBookMsg.ts b/ets2panda/linter/lib/CookBookMsg.ts index b76ff8f1..c4574292 100644 --- a/ets2panda/linter/lib/CookBookMsg.ts +++ b/ets2panda/linter/lib/CookBookMsg.ts @@ -16,7 +16,7 @@ export const cookBookMsg: string[] = []; export const cookBookTag: string[] = []; -for (let i = 0; i <= 151; i++) { +for (let i = 0; i <= 157; i++) { cookBookMsg[i] = ''; } @@ -178,3 +178,7 @@ 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[154] = ''; +cookBookTag[155] = ''; +cookBookTag[156] = ''; +cookBookTag[157] = 'Only imported variables can be captured by "Sendable" classes (arkts-sendable-imported-variables)'; diff --git a/ets2panda/linter/lib/FaultAttrs.ts b/ets2panda/linter/lib/FaultAttrs.ts index 3bdb392b..200c7c82 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.SendableCapturedVars] = new FaultAttributes(157); diff --git a/ets2panda/linter/lib/FaultDesc.ts b/ets2panda/linter/lib/FaultDesc.ts index 073be58d..c5d23b5b 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.SendableCapturedVars] = 'Sendable class captured variables'; diff --git a/ets2panda/linter/lib/Problems.ts b/ets2panda/linter/lib/Problems.ts index c8f528dd..f046d895 100644 --- a/ets2panda/linter/lib/Problems.ts +++ b/ets2panda/linter/lib/Problems.ts @@ -95,6 +95,7 @@ export enum FaultID { ImportAfterStatement, EsObjectType, SendableClassInheritance, + SendableCapturedVars, // this should always be last enum LAST_ID } diff --git a/ets2panda/linter/lib/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts index 67c5e211..316c18f6 100644 --- a/ets2panda/linter/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -756,6 +756,10 @@ export class TypeScriptLinter { ); this.handleDeclarationInferredType(node); this.handleDefiniteAssignmentAssertion(node); + // check captured variables for sendable class + if (TsUtils.hasSendableDecorator(node.parent as ts.ClassDeclaration)) { + this.scanCapturedVarsInSendableScope(node); + } } private handlePropertyAssignment(node: ts.PropertyAssignment): void { @@ -1466,6 +1470,10 @@ export class TypeScriptLinter { { begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end }, FUNCTION_HAS_NO_RETURN_ERROR_CODE ); + // check captured variables for sendable class + if (TsUtils.hasSendableDecorator(node.parent as ts.ClassDeclaration)) { + this.scanCapturedVarsInSendableScope(node); + } } private handleMethodSignature(node: ts.MethodSignature): void { @@ -1481,6 +1489,10 @@ export class TypeScriptLinter { return; } this.reportThisKeywordsInScope(classStaticBlockDecl.body); + // check captured variables for sendable class + if (TsUtils.hasSendableDecorator(node.parent as ts.ClassDeclaration)) { + this.scanCapturedVarsInSendableScope(node); + } } private handleIdentifier(node: ts.Node): void { @@ -2263,6 +2275,33 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.PrivateIdentifier, autofixable, autofix); } + private scanCapturedVarsInSendableScope( scope: ts.Node ): void { + const callback = (node: ts.Node): void => { + if (node.kind === ts.SyntaxKind.Identifier) { + const symbol = this.tsTypeChecker.getSymbolAtLocation(node); + if (symbol ===undefined) { + this.incrementCounters(node, FaultID.SendableCapturedVars); + return; + } + if ( !(symbol.getFlags() & (ts.SymbolFlags.Variable)) ) + return; + if( this.tsTypeChecker.isArgumentsSymbol(symbol)) + return; + const decl = symbol.getDeclarations(); + if (decl && decl[0].kind === ts.SyntaxKind.Parameter) + return; + if (decl && decl[0].kind === ts.SyntaxKind.PropertyDeclaration) + return; + let declPosition = decl?.[0].getStart(); + if(declPosition && (declPosition >= scope.getStart()) && (declPosition < scope.getEnd()) ) + return; + this.incrementCounters(node, FaultID.SendableCapturedVars); + } + }; + const stopCondition = null; // scan all subtree + forEachNodeInSubtree(scope, callback); + } + private createSyncAutofixes(symbol: ts.Symbol, autofixes: Autofix[]): Autofix[] { if (!this.syncAutofixes.has(symbol)) { this.syncAutofixes.set(symbol, []); diff --git a/ets2panda/linter/test/sendable_captured_variables.ts b/ets2panda/linter/test/sendable_captured_variables.ts new file mode 100644 index 00000000..9c2a02c8 --- /dev/null +++ b/ets2panda/linter/test/sendable_captured_variables.ts @@ -0,0 +1,35 @@ +/* + * 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. + */ + + +import { hfpProfileSwitchMenuStyle } from "./object_literals.ts" + +let local = 'local'; + +@Sendable +class SendableClass { + public p = local; // error + static ps = local; // error + public foo(x: string): string { + let s = local; //error + let ss = this.p + x; + return s + ss; + } + static { + SendableClass.ps = local; //error + let pps = hfpProfileSwitchMenuStyle; + } + +} diff --git a/ets2panda/linter/test/sendable_captured_variables.ts.autofix.json b/ets2panda/linter/test/sendable_captured_variables.ts.autofix.json new file mode 100644 index 00000000..e7ecbbc6 --- /dev/null +++ b/ets2panda/linter/test/sendable_captured_variables.ts.autofix.json @@ -0,0 +1,44 @@ +{ + "nodes": [ + { + "line": 23, + "column": 13, + "problem": "SendableCapturedVars", + "autofixable": false, + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 24, + "column": 17, + "problem": "SendableCapturedVars", + "autofixable": false, + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 26, + "column": 11, + "problem": "SendableCapturedVars", + "autofixable": false, + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 31, + "column": 22, + "problem": "SendableCapturedVars", + "autofixable": false, + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 32, + "column": 7, + "problem": "AnyType", + "autofixable": false, + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_captured_variables.ts.json b/ets2panda/linter/test/sendable_captured_variables.ts.json new file mode 100644 index 00000000..28503629 --- /dev/null +++ b/ets2panda/linter/test/sendable_captured_variables.ts.json @@ -0,0 +1,39 @@ +{ + "nodes": [ + { + "line": 23, + "column": 13, + "problem": "SendableCapturedVars", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 24, + "column": 17, + "problem": "SendableCapturedVars", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 26, + "column": 11, + "problem": "SendableCapturedVars", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 31, + "column": 22, + "problem": "SendableCapturedVars", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 32, + "column": 7, + "problem": "AnyType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + } + ] +} \ No newline at end of file -- Gitee From 6ffdb916b96441700782e04f848d9a5e80c1b4c3 Mon Sep 17 00:00:00 2001 From: Igor Rossinski Date: Fri, 29 Mar 2024 03:55:14 +0300 Subject: [PATCH 4/4] [ArkTS Linter] Update fix for rule#157 captured variables in sendable class Signed-off-by: Igor Rossinski --- ets2panda/linter/lib/TypeScriptLinter.ts | 15 +++---- .../test/sendable_captured_variables.ts | 27 ++++++++++--- ...ndable_captured_variables.ts.autofix.skip} | 0 .../test/sendable_captured_variables.ts.json | 39 ++++++++++++++----- .../test/sendable_captured_variables_lib.d.ts | 16 ++++++++ 5 files changed, 76 insertions(+), 21 deletions(-) rename ets2panda/linter/test/{sendable_captured_variables.ts.autofix.json => sendable_captured_variables.ts.autofix.skip} (100%) create mode 100644 ets2panda/linter/test/sendable_captured_variables_lib.d.ts diff --git a/ets2panda/linter/lib/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts index 28b76685..237f6aa0 100644 --- a/ets2panda/linter/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -2284,30 +2284,31 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.PrivateIdentifier, autofixable, autofix); } - private scanCapturedVarsInSendableScope( scope: ts.Node ): void { + private scanCapturedVarsInSendableScope( scope: ts.Node, ): void { + const inClass = scope.parent; const callback = (node: ts.Node): void => { if (node.kind === ts.SyntaxKind.Identifier) { const symbol = this.tsTypeChecker.getSymbolAtLocation(node); - if (symbol ===undefined) { + if (symbol === undefined) { this.incrementCounters(node, FaultID.SendableCapturedVars); return; } - if ( !(symbol.getFlags() & (ts.SymbolFlags.Variable)) ) - return; + //if ( !(symbol.getFlags() & (ts.SymbolFlags.Variable)) ) + // return; if( this.tsTypeChecker.isArgumentsSymbol(symbol)) return; const decl = symbol.getDeclarations(); - if (decl && decl[0].kind === ts.SyntaxKind.Parameter) - return; if (decl && decl[0].kind === ts.SyntaxKind.PropertyDeclaration) return; + if (decl && decl[0].kind === ts.SyntaxKind.ImportSpecifier) + return; let declPosition = decl?.[0].getStart(); if(declPosition && (declPosition >= scope.getStart()) && (declPosition < scope.getEnd()) ) return; this.incrementCounters(node, FaultID.SendableCapturedVars); } }; - const stopCondition = null; // scan all subtree + // scan all subtree forEachNodeInSubtree(scope, callback); } diff --git a/ets2panda/linter/test/sendable_captured_variables.ts b/ets2panda/linter/test/sendable_captured_variables.ts index 9c2a02c8..84e47cba 100644 --- a/ets2panda/linter/test/sendable_captured_variables.ts +++ b/ets2panda/linter/test/sendable_captured_variables.ts @@ -14,22 +14,39 @@ */ -import { hfpProfileSwitchMenuStyle } from "./object_literals.ts" +import { libPi, libString, libFoo, libClass, libClassVar } from "./sendable_captured_varaibles_lib" let local = 'local'; +function foo(): string { + return "local foo"; +} + +class localClass { } + +let localObj: localClass = {}; + @Sendable class SendableClass { + static pi: number = libPi; + static hdr: string = libString; public p = local; // error - static ps = local; // error + static ps = local; // error + public foo(x: string): string { let s = local; //error - let ss = this.p + x; - return s + ss; + let ss = this.p + x; + s = foo(); // error + s = libFoo(); + return s + ss; } static { SendableClass.ps = local; //error - let pps = hfpProfileSwitchMenuStyle; + let pps: string = libString; + let lc: localClass; //error + lc = localObj; //error + let c: libClass; + c = libClassVar; } } diff --git a/ets2panda/linter/test/sendable_captured_variables.ts.autofix.json b/ets2panda/linter/test/sendable_captured_variables.ts.autofix.skip similarity index 100% rename from ets2panda/linter/test/sendable_captured_variables.ts.autofix.json rename to ets2panda/linter/test/sendable_captured_variables.ts.autofix.skip diff --git a/ets2panda/linter/test/sendable_captured_variables.ts.json b/ets2panda/linter/test/sendable_captured_variables.ts.json index 28503629..b54865af 100644 --- a/ets2panda/linter/test/sendable_captured_variables.ts.json +++ b/ets2panda/linter/test/sendable_captured_variables.ts.json @@ -1,39 +1,60 @@ { "nodes": [ { - "line": 23, + "line": 33, "column": 13, "problem": "SendableCapturedVars", "suggest": "", "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" }, { - "line": 24, - "column": 17, + "line": 34, + "column": 14, "problem": "SendableCapturedVars", "suggest": "", "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" }, { - "line": 26, + "line": 37, "column": 11, "problem": "SendableCapturedVars", "suggest": "", "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" }, { - "line": 31, + "line": 39, + "column": 7, + "problem": "SendableCapturedVars", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 44, + "column": 3, + "problem": "SendableCapturedVars", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 44, "column": 22, "problem": "SendableCapturedVars", "suggest": "", "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" }, { - "line": 32, - "column": 7, - "problem": "AnyType", + "line": 46, + "column": 11, + "problem": "SendableCapturedVars", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" + }, + { + "line": 47, + "column": 8, + "problem": "SendableCapturedVars", "suggest": "", - "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + "rule": "Only imported variables can be captured by \"Sendable\" classes (arkts-sendable-imported-variables)" } ] } \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_captured_variables_lib.d.ts b/ets2panda/linter/test/sendable_captured_variables_lib.d.ts new file mode 100644 index 00000000..d6a904d0 --- /dev/null +++ b/ets2panda/linter/test/sendable_captured_variables_lib.d.ts @@ -0,0 +1,16 @@ +export const libPi = 3.14; +export let libString = "lib string"; + +export function libFoo(): string { + return "libFoo"; +} + +export enum libRGB = {RED, GREEN, BLUE }; + +export class libClass { + public foo(): string { + return "lib class foo"; +} + + +export let libClassVar: libClass; -- Gitee