From a2d979a11c1930d9f4a5bcf75183ea9b61be32e6 Mon Sep 17 00:00:00 2001 From: Vsevolod Pukhov Date: Mon, 4 Sep 2023 20:47:31 +0300 Subject: [PATCH] [ArkTS Linter] Suppres not assignable ESObject in parameter-functionals Signed-off-by: Vsevolod Pukhov --- linter-4.2/src/TypeScriptLinter.ts | 56 ++++++++++++++++++- linter-4.2/test/1unknown_params.ts | 36 ++++++++++++ .../test/1unknown_params.ts.autofix.skip | 0 linter-4.2/test/1unknown_params.ts.relax.json | 25 +++++++++ .../test/1unknown_params.ts.strict.json | 25 +++++++++ linter/src/TypeScriptLinter.ts | 56 ++++++++++++++++++- linter/test/1unknown_params.ts | 36 ++++++++++++ linter/test/1unknown_params.ts.autofix.skip | 0 linter/test/1unknown_params.ts.relax.json | 25 +++++++++ linter/test/1unknown_params.ts.strict.json | 25 +++++++++ linter/test/dynamic_lib.d.ts | 3 + 11 files changed, 281 insertions(+), 6 deletions(-) create mode 100644 linter-4.2/test/1unknown_params.ts create mode 100644 linter-4.2/test/1unknown_params.ts.autofix.skip create mode 100644 linter-4.2/test/1unknown_params.ts.relax.json create mode 100644 linter-4.2/test/1unknown_params.ts.strict.json create mode 100644 linter/test/1unknown_params.ts create mode 100644 linter/test/1unknown_params.ts.autofix.skip create mode 100644 linter/test/1unknown_params.ts.relax.json create mode 100644 linter/test/1unknown_params.ts.strict.json diff --git a/linter-4.2/src/TypeScriptLinter.ts b/linter-4.2/src/TypeScriptLinter.ts index d7622bcc4..bb2d8634a 100644 --- a/linter-4.2/src/TypeScriptLinter.ts +++ b/linter-4.2/src/TypeScriptLinter.ts @@ -836,7 +836,7 @@ export class TypeScriptLinter { if (ts.isPropertyDeclaration(node)) { const decorators = node.decorators; this.handleDecorators(decorators); - this.filterOutStrictDiagnostics(decorators, TsUtils.NON_INITIALIZABLE_PROPERTY_DECORATORS, + this.filterOutDecoratorsDiagnostics(decorators, TsUtils.NON_INITIALIZABLE_PROPERTY_DECORATORS, {begin: propName.getStart(), end: propName.getStart()}, TsUtils.PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE); this.handleDeclarationInferredType(node); @@ -844,7 +844,7 @@ export class TypeScriptLinter { } } - private filterOutStrictDiagnostics( + private filterOutDecoratorsDiagnostics( decorators: readonly ts.Decorator[] | undefined, expectedDecorators: readonly string[], range: {begin: number, end: number}, @@ -897,6 +897,34 @@ export class TypeScriptLinter { } } + private filterStrictDiagnostics(range: { begin: number, end: number }, code: number, + chainCheck: (n: string | ts.DiagnosticMessageChain) => boolean): boolean { + if (!this.tscStrictDiagnostics || !this.sourceFile) { + return false; + } + let file = path.normalize(this.sourceFile.fileName); + let tscDiagnostics = this.tscStrictDiagnostics.get(file) + if (!tscDiagnostics) { + return false; + } + + const checkDiagnostic = (val: ts.Diagnostic) => { + if (val.code !== code) { + return true; + } + if (val.start === undefined || val.start < range.begin || val.start > range.end) { + return true; + } + return chainCheck(val.messageText); + }; + + if (tscDiagnostics.every(checkDiagnostic)) { + return false; + } + this.tscStrictDiagnostics.set(file, tscDiagnostics.filter(checkDiagnostic)); + return true; + } + private handleFunctionExpression(node: ts.Node) { const funcExpr = node as ts.FunctionExpression; const isGenerator = funcExpr.asteriskToken !== undefined; @@ -1511,7 +1539,7 @@ export class TypeScriptLinter { this.handleDecorators(tsMethodDecl.decorators); - this.filterOutStrictDiagnostics(tsMethodDecl.decorators, TsUtils.NON_RETURN_FUNCTION_DECORATORS, + this.filterOutDecoratorsDiagnostics(tsMethodDecl.decorators, TsUtils.NON_RETURN_FUNCTION_DECORATORS, {begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end}, TsUtils.FUNCTION_HAS_NO_RETURN_ERROR_CODE); } @@ -1827,6 +1855,7 @@ export class TypeScriptLinter { this.handleGenericCallWithNoTypeArgs(tsCallExpr); this.handleStructIdentAndUndefinedInArgs(tsCallExpr); this.handleStdlibAPICall(tsCallExpr); + this.handleLibraryTypeCall(tsCallExpr); } private handleImportCall(tsCallExpr: ts.CallExpression) { @@ -1997,6 +2026,27 @@ export class TypeScriptLinter { } } + private handleLibraryTypeCall(callExpr: ts.CallExpression) { + // Permit for restricted callable types? + // if (!this.tsUtils.isLibraryType(this.tsTypeChecker.getTypeAtLocation(callExpr.expression))) { + // return; + // } + const TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE = 2322; + const TYPE_UNKNOWN_ANY_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'unknown' is not assignable to type '.*'.$/; + const ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE = 2345; + + const chainCheck = (n: ts.DiagnosticMessageChain): boolean => { + if (n.code == TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE && + n.messageText.match(TYPE_UNKNOWN_ANY_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE)) { + return false; + } + return n.next == undefined ? true : chainCheck(n.next[0]); + }; + this.filterStrictDiagnostics({ begin: callExpr.pos, end: callExpr.end }, + ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE, + (msg) => (typeof msg == 'string' ? true : chainCheck(msg))); + } + private handleNewExpression(node: ts.Node) { let tsNewExpr = node as ts.NewExpression; this.handleGenericCallWithNoTypeArgs(tsNewExpr); diff --git a/linter-4.2/test/1unknown_params.ts b/linter-4.2/test/1unknown_params.ts new file mode 100644 index 000000000..0a2b1d612 --- /dev/null +++ b/linter-4.2/test/1unknown_params.ts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022-2023 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 { applyToUnknown, fooExecute } from "./dynamic_lib" + +export declare function applyToUnknownETS(fn: (a: unknown) => void); + +function printArg(arg: number): number { + console.log(arg); + return arg; +} + +function main(): void { + applyToUnknown((x: number) => { }); + + applyToUnknownETS((x: number) => { }); + + fooExecute(printArg, 1).then((value: number) => { + console.log(value); + }); + fooExecute(printArg, 1).then((value: Object[]) => { + console.log(value); + }); +} diff --git a/linter-4.2/test/1unknown_params.ts.autofix.skip b/linter-4.2/test/1unknown_params.ts.autofix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/linter-4.2/test/1unknown_params.ts.relax.json b/linter-4.2/test/1unknown_params.ts.relax.json new file mode 100644 index 000000000..8f486a966 --- /dev/null +++ b/linter-4.2/test/1unknown_params.ts.relax.json @@ -0,0 +1,25 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 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": 18, + "column": 51, + "problem": "UnknownType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + } + ] +} \ No newline at end of file diff --git a/linter-4.2/test/1unknown_params.ts.strict.json b/linter-4.2/test/1unknown_params.ts.strict.json new file mode 100644 index 000000000..8f486a966 --- /dev/null +++ b/linter-4.2/test/1unknown_params.ts.strict.json @@ -0,0 +1,25 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 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": 18, + "column": 51, + "problem": "UnknownType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + } + ] +} \ No newline at end of file diff --git a/linter/src/TypeScriptLinter.ts b/linter/src/TypeScriptLinter.ts index ee117b90f..e55996ed9 100644 --- a/linter/src/TypeScriptLinter.ts +++ b/linter/src/TypeScriptLinter.ts @@ -665,14 +665,14 @@ export class TypeScriptLinter { if (ts.isPropertyDeclaration(node)) { const decorators = ts.getDecorators(node); this.handleDecorators(decorators); - this.filterOutStrictDiagnostics(decorators, NON_INITIALIZABLE_PROPERTY_DECORATORS, + this.filterOutDecoratorsDiagnostics(decorators, NON_INITIALIZABLE_PROPERTY_DECORATORS, {begin: propName.getStart(), end: propName.getStart()}, PROPERTY_HAS_NO_INITIALIZER_ERROR_CODE); this.handleDeclarationInferredType(node); this.handleDefiniteAssignmentAssertion(node); } } - private filterOutStrictDiagnostics( + private filterOutDecoratorsDiagnostics( decorators: readonly ts.Decorator[] | undefined, expectedDecorators: readonly string[], range: {begin: number, end: number}, @@ -719,6 +719,34 @@ export class TypeScriptLinter { } } + private filterStrictDiagnostics(range: { begin: number, end: number }, code: number, + chainCheck: (n: string | ts.DiagnosticMessageChain) => boolean): boolean { + if (!this.tscStrictDiagnostics || !this.sourceFile) { + return false; + } + let file = path.normalize(this.sourceFile.fileName); + let tscDiagnostics = this.tscStrictDiagnostics.get(file) + if (!tscDiagnostics) { + return false; + } + + const checkDiagnostic = (val: ts.Diagnostic) => { + if (val.code !== code) { + return true; + } + if (val.start === undefined || val.start < range.begin || val.start > range.end) { + return true; + } + return chainCheck(val.messageText); + }; + + if (tscDiagnostics.every(checkDiagnostic)) { + return false; + } + this.tscStrictDiagnostics.set(file, tscDiagnostics.filter(checkDiagnostic)); + return true; + } + private handleFunctionExpression(node: ts.Node) { const funcExpr = node as ts.FunctionExpression; const isGenerator = funcExpr.asteriskToken !== undefined; @@ -1186,7 +1214,7 @@ export class TypeScriptLinter { this.handleDecorators(ts.getDecorators(tsMethodDecl)); - this.filterOutStrictDiagnostics(ts.getDecorators(tsMethodDecl), NON_RETURN_FUNCTION_DECORATORS, + this.filterOutDecoratorsDiagnostics(ts.getDecorators(tsMethodDecl), NON_RETURN_FUNCTION_DECORATORS, {begin: tsMethodDecl.parameters.end, end: tsMethodDecl.body?.getStart() ?? tsMethodDecl.parameters.end}, FUNCTION_HAS_NO_RETURN_ERROR_CODE); } @@ -1407,6 +1435,7 @@ export class TypeScriptLinter { this.handleGenericCallWithNoTypeArgs(tsCallExpr); this.handleStructIdentAndUndefinedInArgs(tsCallExpr); this.handleStdlibAPICall(tsCallExpr); + this.handleLibraryTypeCall(tsCallExpr); } private handleImportCall(tsCallExpr: ts.CallExpression) { @@ -1541,6 +1570,27 @@ export class TypeScriptLinter { } } + private handleLibraryTypeCall(callExpr: ts.CallExpression) { + // Permit for restricted callable types? + // if (!this.tsUtils.isLibraryType(this.tsTypeChecker.getTypeAtLocation(callExpr.expression))) { + // return; + // } + const TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE = 2322; + const TYPE_UNKNOWN_ANY_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'unknown' is not assignable to type '.*'.$/; + const ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE = 2345; + + const chainCheck = (n: ts.DiagnosticMessageChain): boolean => { + if (n.code == TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE && + n.messageText.match(TYPE_UNKNOWN_ANY_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE)) { + return false; + } + return n.next == undefined ? true : chainCheck(n.next[0]); + }; + this.filterStrictDiagnostics({ begin: callExpr.pos, end: callExpr.end }, + ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE, + (msg) => (typeof msg == 'string' ? true : chainCheck(msg))); + } + private handleNewExpression(node: ts.Node) { let tsNewExpr = node as ts.NewExpression; this.handleGenericCallWithNoTypeArgs(tsNewExpr); diff --git a/linter/test/1unknown_params.ts b/linter/test/1unknown_params.ts new file mode 100644 index 000000000..0a2b1d612 --- /dev/null +++ b/linter/test/1unknown_params.ts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022-2023 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 { applyToUnknown, fooExecute } from "./dynamic_lib" + +export declare function applyToUnknownETS(fn: (a: unknown) => void); + +function printArg(arg: number): number { + console.log(arg); + return arg; +} + +function main(): void { + applyToUnknown((x: number) => { }); + + applyToUnknownETS((x: number) => { }); + + fooExecute(printArg, 1).then((value: number) => { + console.log(value); + }); + fooExecute(printArg, 1).then((value: Object[]) => { + console.log(value); + }); +} diff --git a/linter/test/1unknown_params.ts.autofix.skip b/linter/test/1unknown_params.ts.autofix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/linter/test/1unknown_params.ts.relax.json b/linter/test/1unknown_params.ts.relax.json new file mode 100644 index 000000000..8f486a966 --- /dev/null +++ b/linter/test/1unknown_params.ts.relax.json @@ -0,0 +1,25 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 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": 18, + "column": 51, + "problem": "UnknownType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + } + ] +} \ No newline at end of file diff --git a/linter/test/1unknown_params.ts.strict.json b/linter/test/1unknown_params.ts.strict.json new file mode 100644 index 000000000..8f486a966 --- /dev/null +++ b/linter/test/1unknown_params.ts.strict.json @@ -0,0 +1,25 @@ +{ + "copyright": [ + "Copyright (c) 2023-2023 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": 18, + "column": 51, + "problem": "UnknownType", + "suggest": "", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)" + } + ] +} \ No newline at end of file diff --git a/linter/test/dynamic_lib.d.ts b/linter/test/dynamic_lib.d.ts index b5eecaeef..e32ce8637 100644 --- a/linter/test/dynamic_lib.d.ts +++ b/linter/test/dynamic_lib.d.ts @@ -11,3 +11,6 @@ export declare interface I2 { f2?: Array; f3?: any; } + +export declare function applyToUnknown(fn: (a: unknown) => void); +export declare function fooExecute(func: Function, ...args: unknown[]): Promise; -- Gitee