diff --git a/ets2panda/linter/.gitignore b/ets2panda/linter/.gitignore index a84c3073e0abc2a9f5b74ed3b52cbc3f7084dbec..26590e3ca2b7477484ae2e04eb3e74159801e0bc 100644 --- a/ets2panda/linter/.gitignore +++ b/ets2panda/linter/.gitignore @@ -5,4 +5,5 @@ dist node_modules package-lock.json panda-tslinter-1.0.0.tgz -coverage/ \ No newline at end of file +coverage/ +test/local diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index 666921f9426e589133e7af1359b8685f52062996..efa5e995bb6cfbce6b97f49b4e64ac7574aaeeda 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -1869,6 +1869,12 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { this.handleMissingReturnType(arrowFunc); } } + if (!ts.isBlock(arrowFunc.body)) { + const contextRetType = this.tsTypeChecker.getContextualType(arrowFunc.body); + if (contextRetType) { + this.checkAssignmentMatching(arrowFunc.body, contextRetType, arrowFunc.body, true); + } + } this.checkDefaultParamBeforeRequired(arrowFunc); this.handleLimitedVoidFunction(arrowFunc); } @@ -1876,8 +1882,8 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { private handleFunctionDeclaration(node: ts.Node): void { // early exit via exception if cancellation was requested this.options.cancellationToken?.throwIfCancellationRequested(); - const tsFunctionDeclaration = node as ts.FunctionDeclaration; + if (!tsFunctionDeclaration.type) { this.handleMissingReturnType(tsFunctionDeclaration); } @@ -7341,10 +7347,46 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } } + isExprReturnedFromAsyncFunction(rhsExpr: ts.Expression | undefined, lhsType: ts.Type): ts.Type | undefined { + void this; + if (!rhsExpr) { + return undefined; + } + + const enclosingFunction = ts.findAncestor(rhsExpr, ts.isFunctionLike); + const isReturnExpr = ts.isReturnStatement(rhsExpr.parent) || ts.isArrowFunction(rhsExpr.parent); + if (!enclosingFunction) { + return undefined; + } + if (!isReturnExpr) { + return undefined; + } + + if (!TsUtils.hasModifier(enclosingFunction.modifiers, ts.SyntaxKind.AsyncKeyword)) { + return undefined; + } + + const lhsPromiseLikeType = lhsType.isUnion() && lhsType.types.find(TsUtils.isStdPromiseLikeType); + if (!lhsPromiseLikeType) { + return undefined; + } + + if (!TsUtils.isTypeReference(lhsPromiseLikeType) || !lhsPromiseLikeType.typeArguments?.length) { + return undefined; + } + return lhsPromiseLikeType.typeArguments[0]; + } + private handleArrayTypeImmutable(node: ts.Node, lhsType: ts.Type, rhsType: ts.Type, rhsExpr?: ts.Expression): void { if (!this.options.arkts2) { return; } + + const possibleLhsType = this.isExprReturnedFromAsyncFunction(rhsExpr, lhsType); + if (possibleLhsType) { + lhsType = possibleLhsType; + } + const isArray = this.tsUtils.isArray(lhsType) && this.tsUtils.isArray(rhsType); const isTuple = this.tsUtils.isOrDerivedFrom(lhsType, TsUtils.isTuple) && this.tsUtils.isOrDerivedFrom(rhsType, TsUtils.isTuple); @@ -7357,18 +7399,31 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return; } - if (ts.isAsExpression(node) && ts.isArrayLiteralExpression(node.expression)) { - node.expression.elements.forEach((elem) => { - if (elem.kind === ts.SyntaxKind.FalseKeyword || elem.kind === ts.SyntaxKind.TrueKeyword) { - lhsTypeStr = rhsTypeStr.replace(elem.getText(), 'boolean'); - } - }); + const possibleLhsTypeStr = this.checkLhsTypeString(node, rhsTypeStr); + if (possibleLhsTypeStr) { + lhsTypeStr = possibleLhsTypeStr; } + if (lhsTypeStr !== rhsTypeStr) { this.incrementCounters(node, FaultID.ArrayTypeImmutable); } } + private checkLhsTypeString(node: ts.Node, rhsTypeStr: string): string | undefined { + void this; + if (!ts.isAsExpression(node) || !ts.isArrayLiteralExpression(node.expression)) { + return undefined; + } + let lhsTypeStr: string | undefined; + node.expression.elements.forEach((elem) => { + if (elem.kind === ts.SyntaxKind.FalseKeyword || elem.kind === ts.SyntaxKind.TrueKeyword) { + lhsTypeStr = rhsTypeStr.replace(elem.getText(), 'boolean'); + } + }); + + return lhsTypeStr; + } + private isSubtypeByBaseTypesList(baseType: ts.Type, actualType: ts.Type): boolean { if (this.isTypeAssignable(actualType, baseType)) { return true; diff --git a/ets2panda/linter/src/lib/autofixes/Autofixer.ts b/ets2panda/linter/src/lib/autofixes/Autofixer.ts index 243071ad189faa279571e831ffa796b4bf63dc54..f95376ab59f96c4dc40586ec99a7c1962160d4a1 100644 --- a/ets2panda/linter/src/lib/autofixes/Autofixer.ts +++ b/ets2panda/linter/src/lib/autofixes/Autofixer.ts @@ -4187,7 +4187,7 @@ export class Autofixer { return [{ start: newExpr.getStart(), end: newExpr.getEnd(), replacementText }]; } - + fixAppStorageCallExpression(callExpr: ts.CallExpression): Autofix[] | undefined { const varDecl = Autofixer.findParentVariableDeclaration(callExpr); if (!varDecl || varDecl.type) { @@ -4776,13 +4776,13 @@ export class Autofixer { `${expr.getText()}${callExpr.questionDotToken.getText()}unsafeCall` : `${expr.getText()}.unsafeCall`; - return [{ - start: expr.getStart(), - end: hasOptionalChain ? - callExpr.questionDotToken.getEnd() : - expr.getEnd(), - replacementText - }]; + return [ + { + start: expr.getStart(), + end: hasOptionalChain ? callExpr.questionDotToken.getEnd() : expr.getEnd(), + replacementText + } + ]; } private static createBuiltInTypeInitializer(type: ts.TypeReferenceNode): ts.Expression | undefined { diff --git a/ets2panda/linter/src/lib/utils/consts/ArktsWhiteApiPaths.ts b/ets2panda/linter/src/lib/utils/consts/ArktsWhiteApiPaths.ts index 113c4bdd7992092735bc166f897822ce1132c74c..c945fcd33c43ea75cf3dc6385861979004e8f531 100755 --- a/ets2panda/linter/src/lib/utils/consts/ArktsWhiteApiPaths.ts +++ b/ets2panda/linter/src/lib/utils/consts/ArktsWhiteApiPaths.ts @@ -14,4 +14,4 @@ */ export const ARKTS_WHITE_API_PATH_TEXTSTYLE = 'component/styled_string.d.ts'; -export const COMMON_UNION_MEMBER_ACCESS_WHITELIST = new Set(['ArrayBufferLike', 'IteratorResult']); \ No newline at end of file +export const COMMON_UNION_MEMBER_ACCESS_WHITELIST = new Set(['ArrayBufferLike', 'IteratorResult']); diff --git a/ets2panda/linter/src/lib/utils/consts/BuiltinWhiteList.ts b/ets2panda/linter/src/lib/utils/consts/BuiltinWhiteList.ts index db5078a6764ca18bb1bdcb20b15ef93bca3401ea..297b5fba00b1328a2e454f5989ebef0765b9fa2f 100644 --- a/ets2panda/linter/src/lib/utils/consts/BuiltinWhiteList.ts +++ b/ets2panda/linter/src/lib/utils/consts/BuiltinWhiteList.ts @@ -48,18 +48,6 @@ export const BUILTIN_DISABLE_CALLSIGNATURE = [ export const BUILTIN_CONSTRUCTORS = ['Boolean', 'Number', 'Object', 'String']; +export const COLLECTION_TYPES = new Set(['Map', 'Set', 'WeakMap', 'WeakSet']); -export const COLLECTION_TYPES = new Set([ - 'Map', - 'Set', - 'WeakMap', - 'WeakSet' -]); - -export const COLLECTION_METHODS = new Set([ - 'add', - 'delete', - 'get', - 'has', - 'set' -]); \ No newline at end of file +export const COLLECTION_METHODS = new Set(['add', 'delete', 'get', 'has', 'set']); diff --git a/ets2panda/linter/test/main/arkts-array-type-immutable.ets b/ets2panda/linter/test/main/arkts-array-type-immutable.ets index 8f805d822991f5155bfc793f9a0f344ca9c1bc33..3c018c68ab24c13ba22459b3696df7c95d28fe6c 100644 --- a/ets2panda/linter/test/main/arkts-array-type-immutable.ets +++ b/ets2panda/linter/test/main/arkts-array-type-immutable.ets @@ -153,4 +153,20 @@ if (handler.apply) handler.apply(objA, objA, []); let readonlyArr: ReadonlyArray = []; let arr66 = new Array(); -readonlyArr = arr66; //error \ No newline at end of file +readonlyArr = arr66; //error + +let stringArray: string[] = [] +let correctArr: (string | number)[] = [] + +const Foo = (): string[] => stringArray; + +const FooBar = (): (string | number)[] => stringArray; +const Baz = (): (string | number)[] => correctArr; + +async function Foo_a(): Promise<(string| number)[]> { + return stringArray; +} + +async function Foo_b(): Promise<(string| number)[]> { + return correctArr; +} diff --git a/ets2panda/linter/test/main/arkts-array-type-immutable.ets.arkts2.json b/ets2panda/linter/test/main/arkts-array-type-immutable.ets.arkts2.json index e2441885c34ec65cda9f6053db97f9fcbe36c3af..74eb906e2fd654a4e341b370d93e0d7d63548e4d 100644 --- a/ets2panda/linter/test/main/arkts-array-type-immutable.ets.arkts2.json +++ b/ets2panda/linter/test/main/arkts-array-type-immutable.ets.arkts2.json @@ -733,6 +733,26 @@ "suggest": "", "rule": "Array type is immutable in ArkTS1.2 (arkts-array-type-immutable)", "severity": "ERROR" + }, + { + "line": 163, + "column": 43, + "endLine": 163, + "endColumn": 54, + "problem": "ArrayTypeImmutable", + "suggest": "", + "rule": "Array type is immutable in ArkTS1.2 (arkts-array-type-immutable)", + "severity": "ERROR" + }, + { + "line": 167, + "column": 5, + "endLine": 167, + "endColumn": 24, + "problem": "ArrayTypeImmutable", + "suggest": "", + "rule": "Array type is immutable in ArkTS1.2 (arkts-array-type-immutable)", + "severity": "ERROR" } ] -} \ No newline at end of file +}