diff --git a/ets2panda/linter/lib/CookBookMsg.ts b/ets2panda/linter/lib/CookBookMsg.ts index be69b15abb678256a66657e5bf93f2c712ec7e29..817375b29e22acb8fc1924ac2d825fefc8b9da44 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 <= 171; i++) { +for (let i = 0; i <= 181; i++) { cookBookMsg[i] = ''; } @@ -223,3 +223,5 @@ cookBookTag[179] = 'Casting "Non-sendable" function to "Sendable" typeAlias is not allowed (arkts-sendable-function-as-expr)'; cookBookTag[180] = 'The "@Sendable" decorator can only be used on "class", "function", "typeAlias" (arkts-sendable-decorator-limited)'; +cookBookTag[181] = + 'Only "Sendable" object can be assigned to "Sendable Readonly/Partial/Required" (arkts-sendable-tools-limited)'; diff --git a/ets2panda/linter/lib/FaultAttrs.ts b/ets2panda/linter/lib/FaultAttrs.ts index 9c3aa5f5193d22f0b231ae2d7c2d2cff12dc01aa..adf017f830381f8c12939c84df8e1656c6ad73e5 100644 --- a/ets2panda/linter/lib/FaultAttrs.ts +++ b/ets2panda/linter/lib/FaultAttrs.ts @@ -133,3 +133,8 @@ faultsAttrs[FaultID.SendableFunctionOverloadDecorator] = new FaultAttributes(177 faultsAttrs[FaultID.SendableFunctionProperty] = new FaultAttributes(178); faultsAttrs[FaultID.SendableFunctionAsExpr] = new FaultAttributes(179); faultsAttrs[FaultID.SendableDecoratorLimited] = new FaultAttributes(180); +faultsAttrs[FaultID.SendableToolsLimited] = new FaultAttributes(181, ProblemSeverity.WARNING); +faultsAttrs[FaultID.SendableGenericTypesWarning] = new FaultAttributes(156, ProblemSeverity.WARNING); +faultsAttrs[FaultID.SharedModuleExportsWarning] = new FaultAttributes(163, ProblemSeverity.WARNING); +faultsAttrs[FaultID.SendableCapturedVarsWarning] = new FaultAttributes(157, ProblemSeverity.WARNING); +faultsAttrs[FaultID.SendableFunctionImportedVariablesWarning] = new FaultAttributes(172, ProblemSeverity.WARNING); diff --git a/ets2panda/linter/lib/FaultDesc.ts b/ets2panda/linter/lib/FaultDesc.ts index 9385335fcb88d0c5d541838473854280651444f4..cf957378e134571814e533742f84ba5a628d7fb0 100644 --- a/ets2panda/linter/lib/FaultDesc.ts +++ b/ets2panda/linter/lib/FaultDesc.ts @@ -126,3 +126,8 @@ faultDesc[FaultID.SendableFunctionOverloadDecorator] = 'Sendable function overlo faultDesc[FaultID.SendableFunctionProperty] = 'Sendable function property'; faultDesc[FaultID.SendableFunctionAsExpr] = 'Sendable function as expr'; faultDesc[FaultID.SendableDecoratorLimited] = 'Sendable decorator limited'; +faultDesc[FaultID.SendableToolsLimited] = 'Sendable tools limited'; +faultDesc[FaultID.SendableGenericTypesWarning] = 'Sendable return generic'; +faultDesc[FaultID.SharedModuleExportsWarning] = 'Shared module exports'; +faultDesc[FaultID.SendableCapturedVarsWarning] = 'Sendable class captured variables'; +faultDesc[FaultID.SendableFunctionImportedVariablesWarning] = 'Sendable function imported variables'; diff --git a/ets2panda/linter/lib/Problems.ts b/ets2panda/linter/lib/Problems.ts index 4ae1ff949d221c1610c89e56d8a4c7d674e0416f..47769528f5ef2b65d626e12d092ea6a464e8ef6e 100644 --- a/ets2panda/linter/lib/Problems.ts +++ b/ets2panda/linter/lib/Problems.ts @@ -122,6 +122,11 @@ export enum FaultID { SendableFunctionProperty, SendableFunctionAsExpr, SendableDecoratorLimited, + SendableToolsLimited, + SendableGenericTypesWarning, + SharedModuleExportsWarning, + SendableCapturedVarsWarning, + SendableFunctionImportedVariablesWarning, // this should always be last enum LAST_ID } diff --git a/ets2panda/linter/lib/TypeScriptLinter.ts b/ets2panda/linter/lib/TypeScriptLinter.ts index fb33ff4d75bcf0947cd4095b3625c07a72b71d31..2b1a7a84698407736f93cbccb6b16abe0d8648ba 100644 --- a/ets2panda/linter/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/lib/TypeScriptLinter.ts @@ -59,7 +59,7 @@ import { import { SupportedStdCallApiChecker } from './utils/functions/SupportedStdCallAPI'; import { identiferUseInValueContext } from './utils/functions/identiferUseInValueContext'; import { isAssignmentOperator } from './utils/functions/isAssignmentOperator'; -import { SENDABLE_DECORATOR, SENDABLE_DECORATOR_NODES } from './utils/consts/SendableAPI'; +import { SENDABLE_DECORATOR, SENDABLE_DECORATOR_NODES, SENDABLE_CLOSURE_DECLS } from './utils/consts/SendableAPI'; export function consoleLog(...args: unknown[]): void { if (TypeScriptLinter.ideMode) { @@ -94,6 +94,8 @@ export class TypeScriptLinter { autofixer: Autofixer | undefined; + private fileExportSendableDeclCaches: Set | undefined; + private sourceFile?: ts.SourceFile; private static sharedModulesCache: Map; static filteredDiagnosticMessages: Set; @@ -984,7 +986,8 @@ export class TypeScriptLinter { this.scanCapturedVarsInSendableScope( tsFunctionDeclaration, tsFunctionDeclaration, - FaultID.SendableFunctionImportedVariables + FaultID.SendableFunctionImportedVariables, + FaultID.SendableFunctionImportedVariablesWarning ); } } @@ -1373,7 +1376,12 @@ export class TypeScriptLinter { // Check captured variables for sendable class if (isSendableClass) { tsClassDecl.members.forEach((classMember) => { - this.scanCapturedVarsInSendableScope(classMember, tsClassDecl, FaultID.SendableCapturedVars); + this.scanCapturedVarsInSendableScope( + classMember, + tsClassDecl, + FaultID.SendableCapturedVars, + FaultID.SendableCapturedVarsWarning + ); }); } @@ -1811,6 +1819,7 @@ export class TypeScriptLinter { if (callSignature !== undefined && !this.tsUtils.isLibrarySymbol(calleeSym)) { this.handleGenericCallWithNoTypeArgs(tsCallExpr, callSignature); this.handleStructIdentAndUndefinedInArgs(tsCallExpr, callSignature); + this.checkCallExpressionReturnType(tsCallExpr, callSignature); } this.handleLibraryTypeCall(tsCallExpr); @@ -1822,6 +1831,24 @@ export class TypeScriptLinter { } } + private checkCallExpressionReturnType(tsCallExpr: ts.CallExpression, callSignature: ts.Signature): void { + const declReturnType = callSignature.declaration?.type; + if (declReturnType && ts.isTypeReferenceNode(declReturnType)) { + + /* + * If there is no typeParamter in the return type, there is no need to check, + * avoid duplication of checks associated with handleTypeReference + */ + if (!this.tsUtils.typeNodeContainsTypeParameter(declReturnType)) { + return; + } + } + + if (this.tsUtils.isWrongSendableTypeArguments(callSignature.getReturnType())) { + this.incrementCounters(tsCallExpr, FaultID.SendableGenericTypesWarning); + } + } + private handleEtsComponentExpression(node: ts.Node): void { // for all the checks we make EtsComponentExpression is compatible with the CallExpression const etsComponentExpression = node as ts.CallExpression; @@ -2523,7 +2550,12 @@ export class TypeScriptLinter { this.incrementCounters(node, FaultID.ObjectTypeLiteral, autofix); } - private scanCapturedVarsInSendableScope(startNode: ts.Node, scope: ts.Node, faultId: FaultID): void { + private scanCapturedVarsInSendableScope( + startNode: ts.Node, + scope: ts.Node, + faultId: FaultID, + warningFaultId: FaultID + ): void { const callback = (node: ts.Node): void => { // Namespace import will introduce closure in the es2abc compiler stage if (!ts.isIdentifier(node) || this.checkNamespaceImportVar(node)) { @@ -2540,7 +2572,7 @@ export class TypeScriptLinter { return; } - this.checkLocalDecl(node, scope, faultId); + this.checkLocalDecl(node, scope, faultId, warningFaultId); }; // Type nodes should not checked because no closure will be introduced const stopCondition = (node: ts.Node): boolean => { @@ -2553,7 +2585,7 @@ export class TypeScriptLinter { forEachNodeInSubtree(startNode, callback, stopCondition); } - private checkLocalDecl(node: ts.Identifier, scope: ts.Node, faultId: FaultID): void { + private checkLocalDecl(node: ts.Identifier, scope: ts.Node, faultId: FaultID, warningFaultId: FaultID): void { const trueSym = this.tsUtils.trueSymbolAtLocation(node); // Sendable decorator should be used in method of Sendable classes if (trueSym === undefined) { @@ -2567,15 +2599,17 @@ export class TypeScriptLinter { const declarations = trueSym.getDeclarations(); if (declarations?.length) { - this.checkLocalDeclWithSendableClosure(node, scope, declarations[0], faultId); + this.checkLocalDeclWithSendableClosure(node, scope, declarations[0], faultId, warningFaultId); } } + // eslint-disable-next-line complexity private checkLocalDeclWithSendableClosure( node: ts.Identifier, scope: ts.Node, decl: ts.Declaration, - faultId: FaultID + faultId: FaultID, + warningFaultId: FaultID ): void { const declPosition = decl.getStart(); if ( @@ -2584,6 +2618,9 @@ export class TypeScriptLinter { ) { return; } + if (this.isFileExportSendableDecl(decl)) { + this.incrementCounters(node, warningFaultId); + } if (this.isTopSendableClosure(decl)) { return; } @@ -2636,6 +2673,20 @@ export class TypeScriptLinter { return false; } + isFileExportSendableDecl(node: ts.Node): boolean { + if (!ts.isClassDeclaration(node) && !ts.isFunctionDeclaration(node)) { + return false; + } + const sourceFile = node.getSourceFile(); + if (!sourceFile) { + return false; + } + if (!this.fileExportSendableDeclCaches) { + this.fileExportSendableDeclCaches = this.tsUtils.searchFileExportDecl(sourceFile, SENDABLE_CLOSURE_DECLS); + } + return !!this.fileExportSendableDeclCaches.has(node); + } + lint(sourceFile: ts.SourceFile): void { if (this.enableAutofix) { this.autofixer = new Autofixer(this.tsTypeChecker, this.tsUtils, sourceFile, this.cancellationToken); @@ -2643,6 +2694,7 @@ export class TypeScriptLinter { this.walkedComments.clear(); this.sourceFile = sourceFile; + this.fileExportSendableDeclCaches = undefined; this.visitSourceFile(this.sourceFile); this.handleCommentDirectives(this.sourceFile); } @@ -2742,6 +2794,11 @@ export class TypeScriptLinter { // check that 'sendable typeAlias' is assigned correctly if (this.tsUtils.isWrongSendableFunctionAssignment(lhsType, rhsType)) { this.incrementCounters(field, FaultID.SendableFunctionAssignment); + return; + } + + if (this.tsUtils.isWrongSendableToolsAssignment(lhsType, rhsType)) { + this.incrementCounters(field, FaultID.SendableToolsLimited); } const isStrict = this.tsUtils.needStrictMatchType(lhsType, rhsType); // 'isNewStructuralCheck' means that this assignment scenario was previously omitted, so only strict matches are checked now diff --git a/ets2panda/linter/lib/utils/TsUtils.ts b/ets2panda/linter/lib/utils/TsUtils.ts index 77eb76f7ec6616dbbca679575106b041b7b22345..6dd0f26453a13de2d03f583bb2ef8ce902d1a378 100644 --- a/ets2panda/linter/lib/utils/TsUtils.ts +++ b/ets2panda/linter/lib/utils/TsUtils.ts @@ -312,6 +312,20 @@ export class TsUtils { ); } + static isObjectLiteral(tsType: ts.Type): tsType is ts.TypeReference { + return ( + (tsType.getFlags() & ts.TypeFlags.Object) !== 0 && + ((tsType as ts.ObjectType).objectFlags & ts.ObjectFlags.ObjectLiteral) !== 0 + ); + } + + static isArrayLiteral(tsType: ts.Type): tsType is ts.TypeReference { + return ( + (tsType.getFlags() & ts.TypeFlags.Object) !== 0 && + ((tsType as ts.ObjectType).objectFlags & ts.ObjectFlags.ArrayLiteral) !== 0 + ); + } + static isPrototypeSymbol(symbol: ts.Symbol | undefined): boolean { return !!symbol && !!symbol.flags && (symbol.flags & ts.SymbolFlags.Prototype) !== 0; } @@ -1877,6 +1891,11 @@ export class TsUtils { if (sym && sym.getFlags() & ts.SymbolFlags.TypeAlias) { const typeDecl = TsUtils.getDeclaration(sym); if (typeDecl && ts.isTypeAliasDeclaration(typeDecl)) { + // Recursion here will lose generic parameters, So make a judgment before recursion + const type: ts.Type = this.tsTypeChecker.getTypeFromTypeNode(typeNode); + if (this.isToolsTypeAlias(type)) { + return this.isSendableToolsTypeAlias(type); + } return this.isSendableTypeNode(typeDecl.type); } } @@ -1909,6 +1928,9 @@ export class TsUtils { if (TsUtils.isSendableFunction(type)) { return true; } + if (this.isSendableToolsTypeAlias(type)) { + return true; + } return this.isSendableClassOrInterface(type); } @@ -2597,4 +2619,123 @@ export class TsUtils { } return false; } + + isWrongSendableToolsAssignment(lhsType: ts.Type, rhsType: ts.Type): boolean { + // eslint-disable-next-line no-param-reassign + lhsType = this.getNonNullableType(lhsType); + // eslint-disable-next-line no-param-reassign + rhsType = this.getNonNullableType(rhsType); + + if (!this.hasSendableToolsTypeAlias(lhsType)) { + return false; + } + + if (rhsType.isUnion()) { + return rhsType.types.some((compType) => { + return this.isInvalidSendableToolsAssignmentType(compType); + }); + } + return this.isInvalidSendableToolsAssignmentType(rhsType); + } + + private isInvalidSendableToolsAssignmentType(type: ts.Type): boolean { + if (TsUtils.isObjectLiteral(type) || TsUtils.isArrayLiteral(type)) { + return true; + } + let checkType; + if (this.isToolsTypeAlias(type) && type.aliasTypeArguments?.length === 1) { + checkType = TsUtils.reduceReference(type.aliasTypeArguments[0]); + } else { + checkType = type; + } + return checkType.isClassOrInterface() && !this.isSendableClassOrInterface(checkType); + } + + // The properties of sendable are allowed to be modified with 'Readonly', 'Partial', and 'Required'. + isSendableToolsTypeAlias(type: ts.Type): boolean { + if (!this.isToolsTypeAlias(type)) { + return false; + } + return type.aliasTypeArguments?.length === 1 && this.isSendableClassOrInterface(type.aliasTypeArguments[0]); + } + + isToolsTypeAlias(type: ts.Type): boolean { + return this.isStdReadonlyType(type) || this.isStdPartialType(type) || this.isStdRequiredType(type); + } + + hasSendableToolsTypeAlias(type: ts.Type): boolean { + if (type.isUnion()) { + return type.types.some((compType) => { + return this.hasSendableToolsTypeAlias(compType); + }); + } + return this.isSendableToolsTypeAlias(type); + } + + // SendableType with generic arguments other than 'sendable data type' is not allowed. + isWrongSendableTypeArguments(type: ts.Type): boolean { + if (type.isUnion()) { + return type.types.some((compType) => { + return this.isWrongSendableTypeArguments(compType); + }); + } + if (!TsUtils.isTypeReference(type) || !type.typeArguments?.length || !this.isSendableClassOrInterface(type)) { + return false; + } + return type.typeArguments.some((compType) => { + return !this.isSendableType(compType); + }); + } + + typeNodeContainsTypeParameter(type: ts.TypeNode): boolean { + return TsUtils.typeContainsTypeParameter(this.tsTypeChecker.getTypeAtLocation(type)); + } + + static typeContainsTypeParameter(type: ts.Type): boolean { + if (!TsUtils.isTypeReference(type) || !type.typeArguments?.length) { + return false; + } + return type.typeArguments.some((compType) => { + return compType.flags === ts.TypeFlags.TypeParameter; + }); + } + + // Search for and save the exported declaration in the specified file, re-exporting another module will not be included. + searchFileExportDecl(sourceFile: ts.SourceFile, targetDecls?: ts.SyntaxKind[]): Set { + const exportDeclSet = new Set(); + const appendDecl = (decl: ts.Node | undefined): void => { + if (!decl || targetDecls && !targetDecls.includes(decl.kind)) { + return; + } + exportDeclSet.add(decl); + }; + + sourceFile.statements.forEach((statement: ts.Statement) => { + if (ts.isExportAssignment(statement)) { + if (statement.isExportEquals) { + return; + } + appendDecl(this.getDeclarationNode(statement.expression)); + } else if (ts.isExportDeclaration(statement)) { + if (!statement.exportClause || !ts.isNamedExports(statement.exportClause)) { + return; + } + statement.exportClause.elements.forEach((specifier) => { + appendDecl(this.getDeclarationNode(specifier.propertyName ?? specifier.name)); + }); + } else if (ts.canHaveModifiers(statement)) { + if (!TsUtils.hasModifier(ts.getModifiers(statement), ts.SyntaxKind.ExportKeyword)) { + return; + } + if (ts.isVariableStatement(statement)) { + for (const exportDecl of statement.declarationList.declarations) { + appendDecl(exportDecl); + } + } else { + appendDecl(statement); + } + } + }); + return exportDeclSet; + } } diff --git a/ets2panda/linter/lib/utils/consts/SendableAPI.ts b/ets2panda/linter/lib/utils/consts/SendableAPI.ts index 887a15d8709c7c3a6d5e67582096121d21e3f0b9..49b369764d7c1bbcb01faa9246ccab9ae1206dc2 100644 --- a/ets2panda/linter/lib/utils/consts/SendableAPI.ts +++ b/ets2panda/linter/lib/utils/consts/SendableAPI.ts @@ -22,3 +22,5 @@ export const SENDABLE_DECORATOR_NODES = [ ts.SyntaxKind.FunctionDeclaration, ts.SyntaxKind.TypeAliasDeclaration ]; + +export const SENDABLE_CLOSURE_DECLS = [ts.SyntaxKind.ClassDeclaration, ts.SyntaxKind.FunctionDeclaration]; diff --git a/ets2panda/linter/test/sendable_call_return.ts b/ets2panda/linter/test/sendable_call_return.ts new file mode 100644 index 0000000000000000000000000000000000000000..b9786dec79bb471b45363c19c117a35c14ee1c4c --- /dev/null +++ b/ets2panda/linter/test/sendable_call_return.ts @@ -0,0 +1,37 @@ +// collections.Array 的 map 方法导致的编译不报错 + +@Sendable +class SendableAnimal {}; +@Sendable +class SendableDog {}; + +@Sendable +class SendableData {}; +class NormalData {}; + +function handle1(p:T): SendableAnimal { + return new SendableAnimal(); +} + +handle1(new SendableData()); // OK, The inferred return type is legal +handle1(new NormalData()); // WARING, The inferred return type is the sendable class with 'non-sendable-type' + +function handle2(p:T) { + return new SendableAnimal; +} +handle2(new SendableData()); // OK +handle2(new NormalData()); // WARING, The inferred return type is the sendable class with 'non-sendable-type' + + +function handle3(p:T) : SendableDog { + return new SendableDog; +} +handle3(new SendableData()); // OK +handle3(new NormalData()); // WARING, If there are more than one generic parameter, all must be valid. + + +function handle4(p:T) : SendableAnimal | SendableDog { + return new SendableAnimal() || new SendableDog(); +} +handle4(new SendableData()); // OK +handle4(new NormalData()); // WARING, If it is a union type, all must be valid. \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_call_return.ts.autofix.skip b/ets2panda/linter/test/sendable_call_return.ts.autofix.skip new file mode 100644 index 0000000000000000000000000000000000000000..64ca30f56b74088cfa6a816932ea1c0a5b9cac4f --- /dev/null +++ b/ets2panda/linter/test/sendable_call_return.ts.autofix.skip @@ -0,0 +1,14 @@ +/* + * 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. + */ \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_call_return.ts.json b/ets2panda/linter/test/sendable_call_return.ts.json new file mode 100644 index 0000000000000000000000000000000000000000..9991e7fc1d84764d29fcc478b4718ae312e139a7 --- /dev/null +++ b/ets2panda/linter/test/sendable_call_return.ts.json @@ -0,0 +1,32 @@ +{ + "nodes": [ + { + "line": 17, + "column": 1, + "problem": "SendableGenericTypesWarning", + "suggest": "", + "rule": "Type arguments of generic \"Sendable\" type must be a \"Sendable\" data type (arkts-sendable-generic-types)" + }, + { + "line": 23, + "column": 1, + "problem": "SendableGenericTypesWarning", + "suggest": "", + "rule": "Type arguments of generic \"Sendable\" type must be a \"Sendable\" data type (arkts-sendable-generic-types)" + }, + { + "line": 30, + "column": 1, + "problem": "SendableGenericTypesWarning", + "suggest": "", + "rule": "Type arguments of generic \"Sendable\" type must be a \"Sendable\" data type (arkts-sendable-generic-types)" + }, + { + "line": 37, + "column": 1, + "problem": "SendableGenericTypesWarning", + "suggest": "", + "rule": "Type arguments of generic \"Sendable\" type must be a \"Sendable\" data type (arkts-sendable-generic-types)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_closure_export.ts b/ets2panda/linter/test/sendable_closure_export.ts new file mode 100644 index 0000000000000000000000000000000000000000..ccf3dd806d7516f19eb1a97106cd8ff15b5c2707 --- /dev/null +++ b/ets2panda/linter/test/sendable_closure_export.ts @@ -0,0 +1,73 @@ + +@Sendable +class Sc1 { + +} + +@Sendable +function sf1() { + +}; + +@Sendable +export class Sc2 { + +} + +@Sendable +export function sf2() { + +}; + +@Sendable +class Sc3 { + +} + +@Sendable +function sf3() { + +}; + +@Sendable +class Sc4 { + +} + +@Sendable +function sf4() { + +}; + +@Sendable +class Sc5 { + +} + +@Sendable +function sf5() { + +}; + + +@Sendable +class SendableClass { + handle() { + new Sc1(); // OK + sf1(); // OK + new Sc2(); // WARING, export decl + sf2(); // WARING, export decl + new Sc3(); // WARING, export { decl } + sf3(); // WARING, export { decl } + new Sc4(); // WARING, export { decl as alias } + sf4(); // WARING, export { decl as alias } + new Sc5(); // WARING, export default decl + } +} + +export {Sc3, sf3, Sc4 as Sc42, sf4 as sf42}; + + +export default Sc5; + +export * from './module'; \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_closure_export.ts.autofix.skip b/ets2panda/linter/test/sendable_closure_export.ts.autofix.skip new file mode 100644 index 0000000000000000000000000000000000000000..64ca30f56b74088cfa6a816932ea1c0a5b9cac4f --- /dev/null +++ b/ets2panda/linter/test/sendable_closure_export.ts.autofix.skip @@ -0,0 +1,14 @@ +/* + * 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. + */ \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_closure_export.ts.json b/ets2panda/linter/test/sendable_closure_export.ts.json new file mode 100644 index 0000000000000000000000000000000000000000..472a7cbdb933308dc5c2bba0b61a77ec9556e524 --- /dev/null +++ b/ets2panda/linter/test/sendable_closure_export.ts.json @@ -0,0 +1,53 @@ +{ + "nodes": [ + { + "line": 58, + "column": 13, + "problem": "SendableCapturedVarsWarning", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" class (arkts-sendable-imported-variables)" + }, + { + "line": 59, + "column": 9, + "problem": "SendableCapturedVarsWarning", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" class (arkts-sendable-imported-variables)" + }, + { + "line": 60, + "column": 13, + "problem": "SendableCapturedVarsWarning", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" class (arkts-sendable-imported-variables)" + }, + { + "line": 61, + "column": 9, + "problem": "SendableCapturedVarsWarning", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" class (arkts-sendable-imported-variables)" + }, + { + "line": 62, + "column": 13, + "problem": "SendableCapturedVarsWarning", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" class (arkts-sendable-imported-variables)" + }, + { + "line": 63, + "column": 9, + "problem": "SendableCapturedVarsWarning", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" class (arkts-sendable-imported-variables)" + }, + { + "line": 64, + "column": 13, + "problem": "SendableCapturedVarsWarning", + "suggest": "", + "rule": "Only imported variables can be captured by \"Sendable\" class (arkts-sendable-imported-variables)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_tools.ts b/ets2panda/linter/test/sendable_tools.ts new file mode 100644 index 0000000000000000000000000000000000000000..95a2a0287dfff85ee8ccda506387c2e8a412e185 --- /dev/null +++ b/ets2panda/linter/test/sendable_tools.ts @@ -0,0 +1,43 @@ +import { SendableAnimal, NormalAnimal} from './sendable_tools_lib'; + + +@Sendable +class SendableClass { + prop1: Readonly = new SendableAnimal(); // OK + prop2: Readonly = 1; // WARING, Generic parameters for tools only support sendable-class or sendable-interface + prop4: Readonly = new NormalAnimal(); // ERROR, Generic parameters for tools only support sendable-class or sendable-interface + prop5: Readonly; // WARING, generics are not supported + + constructor(p:T) { + this.prop5 = p; + } +} + +const a1: Readonly = new SendableAnimal(); // OK +const a2: Readonly = a1; // OK, sendable-tools object can assign to sendable-tools +const a3: Readonly = new NormalAnimal(); // WARING, The assignment object must be sendable-class or sendable-interface or sendable-tools object +const a4: Readonly = {}; // WARING +const a5: Readonly = []; // WARING +const b1: Readonly = new SendableAnimal(); // OK, +const b2: Readonly = {}; // OK +const c1: Readonly | NormalAnimal = new NormalAnimal(); // ERRPR, The union type only needs to contain one sendable to trigger a check. +const c2: Readonly | number = 5; // OK, The scope of check is 'non-sendable class or interface or tools or {} or [] '; + + + +@Sendable +class SendableMain { + prop1: Required; + prop2: Partial; + constructor(p1: Required, p2: Partial) { + this.prop1 = p1; + this.prop2 = p2; + } +} + +const requiredAnimal: Required = new SendableAnimal(); +const partialAnimal: Partial = new SendableAnimal(); + +new SendableMain(new SendableAnimal(), new SendableAnimal()); +new SendableMain(requiredAnimal, partialAnimal); + diff --git a/ets2panda/linter/test/sendable_tools.ts.autofix.skip b/ets2panda/linter/test/sendable_tools.ts.autofix.skip new file mode 100644 index 0000000000000000000000000000000000000000..64ca30f56b74088cfa6a816932ea1c0a5b9cac4f --- /dev/null +++ b/ets2panda/linter/test/sendable_tools.ts.autofix.skip @@ -0,0 +1,14 @@ +/* + * 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. + */ \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_tools.ts.json b/ets2panda/linter/test/sendable_tools.ts.json new file mode 100644 index 0000000000000000000000000000000000000000..85438264004e2f563fa4b5717b00e133246ea26f --- /dev/null +++ b/ets2panda/linter/test/sendable_tools.ts.json @@ -0,0 +1,53 @@ +{ + "nodes": [ + { + "line": 7, + "column": 5, + "problem": "SendablePropType", + "suggest": "", + "rule": "Properties in \"Sendable\" classes and interfaces must have a Sendable data type (arkts-sendable-prop-types)" + }, + { + "line": 8, + "column": 5, + "problem": "SendablePropType", + "suggest": "", + "rule": "Properties in \"Sendable\" classes and interfaces must have a Sendable data type (arkts-sendable-prop-types)" + }, + { + "line": 9, + "column": 5, + "problem": "SendablePropType", + "suggest": "", + "rule": "Properties in \"Sendable\" classes and interfaces must have a Sendable data type (arkts-sendable-prop-types)" + }, + { + "line": 18, + "column": 7, + "problem": "SendableToolsLimited", + "suggest": "", + "rule": "Only \"Sendable\" object can be assigned to \"Sendable Readonly/Partial/Required\" (arkts-sendable-tools-limited)" + }, + { + "line": 19, + "column": 7, + "problem": "SendableToolsLimited", + "suggest": "", + "rule": "Only \"Sendable\" object can be assigned to \"Sendable Readonly/Partial/Required\" (arkts-sendable-tools-limited)" + }, + { + "line": 20, + "column": 7, + "problem": "SendableToolsLimited", + "suggest": "", + "rule": "Only \"Sendable\" object can be assigned to \"Sendable Readonly/Partial/Required\" (arkts-sendable-tools-limited)" + }, + { + "line": 23, + "column": 7, + "problem": "SendableToolsLimited", + "suggest": "", + "rule": "Only \"Sendable\" object can be assigned to \"Sendable Readonly/Partial/Required\" (arkts-sendable-tools-limited)" + } + ] +} \ No newline at end of file diff --git a/ets2panda/linter/test/sendable_tools_lib.d.ts b/ets2panda/linter/test/sendable_tools_lib.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..de1e68e3cca58027194698bacd5c0b70fcc0badc --- /dev/null +++ b/ets2panda/linter/test/sendable_tools_lib.d.ts @@ -0,0 +1,5 @@ +@Sendable +export class SendableAnimal {}; + +export class NormalAnimal {}; +