From 989418fe7a874f257b1237b78e2e1015796469ec Mon Sep 17 00:00:00 2001 From: Konstantin Baladurin Date: Tue, 12 Sep 2023 00:57:19 +0300 Subject: [PATCH] linter: relax type check in case of `undefined` and `null` assignment Signed-off-by: Konstantin Baladurin --- linter-4.2/src/TypeScriptLinter.ts | 17 ++++++-- linter-4.2/test/dynamic_lib.d.ts | 4 ++ linter-4.2/test/null_check_calls.ts | 13 ++++++- .../test/null_check_calls.ts.relax.json | 2 +- .../test/null_check_calls.ts.strict.json | 2 +- linter-4.2/test/undefined_check_calls.ts | 39 +++++++++++++++++++ .../undefined_check_calls.ts.autofix.skip | 0 .../test/undefined_check_calls.ts.relax.json | 25 ++++++++++++ .../test/undefined_check_calls.ts.strict.json | 25 ++++++++++++ linter/src/TypeScriptLinter.ts | 17 ++++++-- linter/test/dynamic_lib.d.ts | 4 ++ linter/test/null_check_calls.ts | 13 ++++++- linter/test/null_check_calls.ts.relax.json | 2 +- linter/test/null_check_calls.ts.strict.json | 2 +- linter/test/undefined_check_calls.ts | 39 +++++++++++++++++++ .../undefined_check_calls.ts.autofix.skip | 0 .../test/undefined_check_calls.ts.relax.json | 25 ++++++++++++ .../test/undefined_check_calls.ts.strict.json | 25 ++++++++++++ 18 files changed, 240 insertions(+), 14 deletions(-) create mode 100644 linter-4.2/test/undefined_check_calls.ts create mode 100644 linter-4.2/test/undefined_check_calls.ts.autofix.skip create mode 100644 linter-4.2/test/undefined_check_calls.ts.relax.json create mode 100644 linter-4.2/test/undefined_check_calls.ts.strict.json create mode 100644 linter/test/undefined_check_calls.ts create mode 100644 linter/test/undefined_check_calls.ts.autofix.skip create mode 100644 linter/test/undefined_check_calls.ts.relax.json create mode 100644 linter/test/undefined_check_calls.ts.strict.json diff --git a/linter-4.2/src/TypeScriptLinter.ts b/linter-4.2/src/TypeScriptLinter.ts index 73f9b3e61..7aad7c66a 100644 --- a/linter-4.2/src/TypeScriptLinter.ts +++ b/linter-4.2/src/TypeScriptLinter.ts @@ -1989,14 +1989,22 @@ export class TypeScriptLinter { } private handleLibraryTypeCall(callExpr: ts.CallExpression) { + // Current approach relates on error code and error message matching and it is quite fragile, + // so this place should be checked thoroughly in the case of typescript upgrade const TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE = 2322; - const TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'unknown' is not assignable to type '.*'.$/; + const TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'unknown' is not assignable to type '.*'\.$/; + const TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'null' is not assignable to type '.*'\.$/; + const TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'undefined' is not assignable to type '.*'\.$/; + const ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE = 2345; - const ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'null' is not assignable to parameter of type '.*'.$/; + const ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'null' is not assignable to parameter of type '.*'\.$/; + const ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'undefined' is not assignable to parameter of type '.*'\.$/; const chainCheck = (n: ts.DiagnosticMessageChain): boolean => { if (n.code == TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE && - n.messageText.match(TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE)) { + (n.messageText.match(TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE) || + n.messageText.match(TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE) || + n.messageText.match(TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE))) { return false; } return n.next == undefined ? true : chainCheck(n.next[0]); @@ -2004,7 +2012,8 @@ export class TypeScriptLinter { const msgCheck = (msg: string): boolean => { if (this.tsUtils.isLibraryType(this.tsTypeChecker.getTypeAtLocation(callExpr.expression))) { - const match = msg.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE); + const match = msg.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || + msg.match(ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE); return !match; } return true; diff --git a/linter-4.2/test/dynamic_lib.d.ts b/linter-4.2/test/dynamic_lib.d.ts index e8a16c272..776c555da 100644 --- a/linter-4.2/test/dynamic_lib.d.ts +++ b/linter-4.2/test/dynamic_lib.d.ts @@ -39,3 +39,7 @@ export interface DynLibIC extends DynLibC {} export interface DynLibII extends DynLibI {} export class DynLibCC extends DynLibC {} export class DynLibCI implements DynLibI {} + +export declare class C1 {} + +export declare function f2(c: C1): void; diff --git a/linter-4.2/test/null_check_calls.ts b/linter-4.2/test/null_check_calls.ts index cce1b98a2..18bcaa037 100644 --- a/linter-4.2/test/null_check_calls.ts +++ b/linter-4.2/test/null_check_calls.ts @@ -14,7 +14,18 @@ */ import { ff } from "./oh_modules/ohos_lib"; -import { f, bar, applyToUnknown, fooExecute } from "./dynamic_lib"; +import { f, f2, bar, applyToUnknown, fooExecute, C1 } from "./dynamic_lib"; + +let a1: C1 = new C1() + +function g1(): C1 | null { + if (a1) { + return a1 + } + return null +} + +f2(g1()) bar(null); bar(null, null); diff --git a/linter-4.2/test/null_check_calls.ts.relax.json b/linter-4.2/test/null_check_calls.ts.relax.json index 3450a6d1d..3a8041365 100644 --- a/linter-4.2/test/null_check_calls.ts.relax.json +++ b/linter-4.2/test/null_check_calls.ts.relax.json @@ -15,7 +15,7 @@ ], "nodes": [ { - "line": 26, + "line": 37, "column": 5, "problem": "StrictDiagnostic", "suggest": "Argument of type 'null' is not assignable to parameter of type 'number[]'.", diff --git a/linter-4.2/test/null_check_calls.ts.strict.json b/linter-4.2/test/null_check_calls.ts.strict.json index 3450a6d1d..3a8041365 100644 --- a/linter-4.2/test/null_check_calls.ts.strict.json +++ b/linter-4.2/test/null_check_calls.ts.strict.json @@ -15,7 +15,7 @@ ], "nodes": [ { - "line": 26, + "line": 37, "column": 5, "problem": "StrictDiagnostic", "suggest": "Argument of type 'null' is not assignable to parameter of type 'number[]'.", diff --git a/linter-4.2/test/undefined_check_calls.ts b/linter-4.2/test/undefined_check_calls.ts new file mode 100644 index 000000000..45b97e57d --- /dev/null +++ b/linter-4.2/test/undefined_check_calls.ts @@ -0,0 +1,39 @@ +/* + * 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 { ff } from "./oh_modules/ohos_lib"; +import { f, f2, bar, applyToUnknown, fooExecute, C1 } from "./dynamic_lib"; + +let a1: C1 = new C1() + +function g1(): C1 | undefined { + if (a1) { + return a1 + } + return undefined +} + +f2(g1()) + +bar(undefined); +bar(undefined, undefined); +applyToUnknown(undefined); +fooExecute(undefined); + +function fff(a: Array): void {} + +fff(undefined); +ff(undefined); +f(undefined); diff --git a/linter-4.2/test/undefined_check_calls.ts.autofix.skip b/linter-4.2/test/undefined_check_calls.ts.autofix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/linter-4.2/test/undefined_check_calls.ts.relax.json b/linter-4.2/test/undefined_check_calls.ts.relax.json new file mode 100644 index 000000000..2340a8686 --- /dev/null +++ b/linter-4.2/test/undefined_check_calls.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": 37, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'undefined' is not assignable to parameter of type 'number[]'.", + "rule": "Argument of type 'undefined' is not assignable to parameter of type 'number[]'." + } + ] +} \ No newline at end of file diff --git a/linter-4.2/test/undefined_check_calls.ts.strict.json b/linter-4.2/test/undefined_check_calls.ts.strict.json new file mode 100644 index 000000000..2340a8686 --- /dev/null +++ b/linter-4.2/test/undefined_check_calls.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": 37, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'undefined' is not assignable to parameter of type 'number[]'.", + "rule": "Argument of type 'undefined' is not assignable to parameter of type 'number[]'." + } + ] +} \ No newline at end of file diff --git a/linter/src/TypeScriptLinter.ts b/linter/src/TypeScriptLinter.ts index 68f26886d..3ed2c916f 100644 --- a/linter/src/TypeScriptLinter.ts +++ b/linter/src/TypeScriptLinter.ts @@ -1564,14 +1564,22 @@ export class TypeScriptLinter { } private handleLibraryTypeCall(callExpr: ts.CallExpression) { + // Current approach relates on error code and error message matching and it is quite fragile, + // so this place should be checked thoroughly in the case of typescript upgrade const TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE = 2322; - const TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'unknown' is not assignable to type '.*'.$/; + const TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'unknown' is not assignable to type '.*'\.$/; + const TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'null' is not assignable to type '.*'\.$/; + const TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type 'undefined' is not assignable to type '.*'\.$/; + const ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE = 2345; - const ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'null' is not assignable to parameter of type '.*'.$/; + const ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'null' is not assignable to parameter of type '.*'\.$/; + const ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = /^Argument of type 'undefined' is not assignable to parameter of type '.*'\.$/; const chainCheck = (n: ts.DiagnosticMessageChain): boolean => { if (n.code == TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE && - n.messageText.match(TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE)) { + (n.messageText.match(TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE) || + n.messageText.match(TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE) || + n.messageText.match(TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE))) { return false; } return n.next == undefined ? true : chainCheck(n.next[0]); @@ -1579,7 +1587,8 @@ export class TypeScriptLinter { const msgCheck = (msg: string): boolean => { if (this.tsUtils.isLibraryType(this.tsTypeChecker.getTypeAtLocation(callExpr.expression))) { - const match = msg.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE); + const match = msg.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || + msg.match(ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE); return !match; } return true; diff --git a/linter/test/dynamic_lib.d.ts b/linter/test/dynamic_lib.d.ts index e8a16c272..776c555da 100644 --- a/linter/test/dynamic_lib.d.ts +++ b/linter/test/dynamic_lib.d.ts @@ -39,3 +39,7 @@ export interface DynLibIC extends DynLibC {} export interface DynLibII extends DynLibI {} export class DynLibCC extends DynLibC {} export class DynLibCI implements DynLibI {} + +export declare class C1 {} + +export declare function f2(c: C1): void; diff --git a/linter/test/null_check_calls.ts b/linter/test/null_check_calls.ts index cce1b98a2..18bcaa037 100644 --- a/linter/test/null_check_calls.ts +++ b/linter/test/null_check_calls.ts @@ -14,7 +14,18 @@ */ import { ff } from "./oh_modules/ohos_lib"; -import { f, bar, applyToUnknown, fooExecute } from "./dynamic_lib"; +import { f, f2, bar, applyToUnknown, fooExecute, C1 } from "./dynamic_lib"; + +let a1: C1 = new C1() + +function g1(): C1 | null { + if (a1) { + return a1 + } + return null +} + +f2(g1()) bar(null); bar(null, null); diff --git a/linter/test/null_check_calls.ts.relax.json b/linter/test/null_check_calls.ts.relax.json index 3450a6d1d..3a8041365 100644 --- a/linter/test/null_check_calls.ts.relax.json +++ b/linter/test/null_check_calls.ts.relax.json @@ -15,7 +15,7 @@ ], "nodes": [ { - "line": 26, + "line": 37, "column": 5, "problem": "StrictDiagnostic", "suggest": "Argument of type 'null' is not assignable to parameter of type 'number[]'.", diff --git a/linter/test/null_check_calls.ts.strict.json b/linter/test/null_check_calls.ts.strict.json index 3450a6d1d..3a8041365 100644 --- a/linter/test/null_check_calls.ts.strict.json +++ b/linter/test/null_check_calls.ts.strict.json @@ -15,7 +15,7 @@ ], "nodes": [ { - "line": 26, + "line": 37, "column": 5, "problem": "StrictDiagnostic", "suggest": "Argument of type 'null' is not assignable to parameter of type 'number[]'.", diff --git a/linter/test/undefined_check_calls.ts b/linter/test/undefined_check_calls.ts new file mode 100644 index 000000000..45b97e57d --- /dev/null +++ b/linter/test/undefined_check_calls.ts @@ -0,0 +1,39 @@ +/* + * 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 { ff } from "./oh_modules/ohos_lib"; +import { f, f2, bar, applyToUnknown, fooExecute, C1 } from "./dynamic_lib"; + +let a1: C1 = new C1() + +function g1(): C1 | undefined { + if (a1) { + return a1 + } + return undefined +} + +f2(g1()) + +bar(undefined); +bar(undefined, undefined); +applyToUnknown(undefined); +fooExecute(undefined); + +function fff(a: Array): void {} + +fff(undefined); +ff(undefined); +f(undefined); diff --git a/linter/test/undefined_check_calls.ts.autofix.skip b/linter/test/undefined_check_calls.ts.autofix.skip new file mode 100644 index 000000000..e69de29bb diff --git a/linter/test/undefined_check_calls.ts.relax.json b/linter/test/undefined_check_calls.ts.relax.json new file mode 100644 index 000000000..2340a8686 --- /dev/null +++ b/linter/test/undefined_check_calls.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": 37, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'undefined' is not assignable to parameter of type 'number[]'.", + "rule": "Argument of type 'undefined' is not assignable to parameter of type 'number[]'." + } + ] +} \ No newline at end of file diff --git a/linter/test/undefined_check_calls.ts.strict.json b/linter/test/undefined_check_calls.ts.strict.json new file mode 100644 index 000000000..2340a8686 --- /dev/null +++ b/linter/test/undefined_check_calls.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": 37, + "column": 5, + "problem": "StrictDiagnostic", + "suggest": "Argument of type 'undefined' is not assignable to parameter of type 'number[]'.", + "rule": "Argument of type 'undefined' is not assignable to parameter of type 'number[]'." + } + ] +} \ No newline at end of file -- Gitee