diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index 7a6dd23d96c159848928bf3d1454328e93c1db4e..4c7b726ac42a83f65f8ae31a695180b8fc0400e6 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -600,13 +600,24 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } } + static ifValidObjectLiteralProperty( + prop: ts.ObjectLiteralElementLike, + objLitExpr: ts.ObjectLiteralExpression + ): boolean { + return ( + ts.isPropertyAssignment(prop) || + ts.isShorthandPropertyAssignment(prop) && + (ts.isCallExpression(objLitExpr.parent) || ts.isNewExpression(objLitExpr.parent)) + ); + } + private handleObjectLiteralProperties( objectLiteralType: ts.Type | undefined, objectLiteralExpr: ts.ObjectLiteralExpression ): void { let objLiteralAutofix: Autofix[] | undefined; const invalidProps = objectLiteralExpr.properties.filter((prop) => { - return !ts.isPropertyAssignment(prop); + return !TypeScriptLinter.ifValidObjectLiteralProperty(prop, objectLiteralExpr); }); if ( @@ -4560,17 +4571,9 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return false; } - private checkRestrictedAPICall(node: ts.Node): void { - if (ts.isCallExpression(node)) { - if (TypeScriptLinter.isReflectAPICall(node)) { - this.incrementCounters(node.parent, FaultID.InteropCallReflect); - return; - } - - const signature = this.tsTypeChecker.getResolvedSignature(node); - if (signature) { - this.checkForForbiddenAPIs(signature, node); - } + private checkRestrictedAPICall(node: ts.CallExpression): void { + if (TypeScriptLinter.isReflectAPICall(node)) { + this.incrementCounters(node.parent, FaultID.InteropCallReflect); } } @@ -4591,11 +4594,31 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { return false; } + private shouldCheckForForbiddenAPI(declaration: ts.SignatureDeclaration | ts.JSDocSignature): boolean { + for (const parameter of declaration.parameters) { + if (ts.isJSDocParameterTag(parameter)) { + continue; + } + const parameterType = this.tsTypeChecker.getTypeAtLocation(parameter); + const parameterTypeString = this.tsTypeChecker.typeToString(parameterType); + + if (parameterTypeString === OBJECT_LITERAL) { + return true; + } + } + + return false; + } + private checkForForbiddenAPIs(callSignature: ts.Signature, tsCallExpr: ts.CallExpression): void { if (!callSignature.declaration) { return; } + if (!this.shouldCheckForForbiddenAPI(callSignature.declaration)) { + return; + } + const functionSymbol = this.getFunctionSymbol(callSignature.declaration); const functionDeclaration = functionSymbol?.valueDeclaration; if (!functionDeclaration) { diff --git a/ets2panda/linter/src/lib/utils/consts/InteropAPI.ts b/ets2panda/linter/src/lib/utils/consts/InteropAPI.ts index 285a75b407baf65f6c5d0096832041ebf3dc57c0..1532865a8a3e77a6521df03145777ac31eafc1ca 100644 --- a/ets2panda/linter/src/lib/utils/consts/InteropAPI.ts +++ b/ets2panda/linter/src/lib/utils/consts/InteropAPI.ts @@ -39,6 +39,7 @@ export const REFLECT_PROPERTIES = [ 'isExtensible', 'preventExtensions' ]; + export const OBJECT_PROPERTIES = [ 'get', 'set', diff --git a/ets2panda/linter/test/interop/object_built_in.ets b/ets2panda/linter/test/interop/object_built_in.ets index 3170f3f087929ed9725c116545ec115437d88e83..3b9dceea070850349fa8069ac8cbd14471aa78c6 100644 --- a/ets2panda/linter/test/interop/object_built_in.ets +++ b/ets2panda/linter/test/interop/object_built_in.ets @@ -25,3 +25,11 @@ export function foo(prx: Object) { } foo(new X()); // Illegal + +export function shouldPass() { + const bar = JSON.parse('{}'); + console.log(Object.keys(bar).length); +} + +shouldPass() + diff --git a/ets2panda/linter/test/interop/object_built_in.ets.arkts2.json b/ets2panda/linter/test/interop/object_built_in.ets.arkts2.json index d9cadcd5d49e623871668199b128d069f02e845d..41a2db7981e1f81b39c3f920c0609d5da79b198b 100644 --- a/ets2panda/linter/test/interop/object_built_in.ets.arkts2.json +++ b/ets2panda/linter/test/interop/object_built_in.ets.arkts2.json @@ -55,14 +55,14 @@ "severity": "ERROR" }, { - "line": 27, - "column": 1, - "endLine": 27, - "endColumn": 14, - "problem": "InteropCallObjectParam", + "line": 30, + "column": 11, + "endLine": 30, + "endColumn": 33, + "problem": "AnyType", "suggest": "", - "rule": "Class type is not compatible with \"Object\" parameter in interop call (arkts-interop-d2s-static-object-on-dynamic-instance)", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)", "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/interop/object_built_in.ets.json b/ets2panda/linter/test/interop/object_built_in.ets.json index d2adea10e51a874fd0c51386e85d2b4552fbecd7..1918dfda6716ab4f49dc866eb6b247a24a5ee221 100644 --- a/ets2panda/linter/test/interop/object_built_in.ets.json +++ b/ets2panda/linter/test/interop/object_built_in.ets.json @@ -45,14 +45,14 @@ "severity": "ERROR" }, { - "line": 27, - "column": 1, - "endLine": 27, - "endColumn": 14, - "problem": "InteropCallObjectParam", + "line": 30, + "column": 11, + "endLine": 30, + "endColumn": 33, + "problem": "AnyType", "suggest": "", - "rule": "Class type is not compatible with \"Object\" parameter in interop call (arkts-interop-d2s-static-object-on-dynamic-instance)", + "rule": "Use explicit types instead of \"any\", \"unknown\" (arkts-no-any-unknown)", "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/interop/reflect_built_in.ets.arkts2.json b/ets2panda/linter/test/interop/reflect_built_in.ets.arkts2.json index 1dd3014588321927823c43ed04ac101322efcd1e..c76a12de83ce4d962372a140309212632aa9a21d 100644 --- a/ets2panda/linter/test/interop/reflect_built_in.ets.arkts2.json +++ b/ets2panda/linter/test/interop/reflect_built_in.ets.arkts2.json @@ -64,26 +64,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 27, - "column": 1, - "endLine": 27, - "endColumn": 14, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, - { - "line": 29, - "column": 1, - "endLine": 29, - "endColumn": 14, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 29, "column": 1, @@ -164,16 +144,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 41, - "column": 1, - "endLine": 41, - "endColumn": 33, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 42, "column": 1, @@ -214,16 +184,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 46, - "column": 1, - "endLine": 46, - "endColumn": 22, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 47, "column": 1, @@ -264,16 +224,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 51, - "column": 1, - "endLine": 51, - "endColumn": 31, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 52, "column": 1, @@ -314,16 +264,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 56, - "column": 1, - "endLine": 56, - "endColumn": 27, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 57, "column": 1, @@ -355,4 +295,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/interop/reflect_built_in.ets.json b/ets2panda/linter/test/interop/reflect_built_in.ets.json index eb39c3a62eade80d254e29b2184277b8f560519d..ef3d440bfb53a748252176bfbf1bb6926a8b1fc4 100644 --- a/ets2panda/linter/test/interop/reflect_built_in.ets.json +++ b/ets2panda/linter/test/interop/reflect_built_in.ets.json @@ -54,26 +54,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 27, - "column": 1, - "endLine": 27, - "endColumn": 14, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, - { - "line": 29, - "column": 1, - "endLine": 29, - "endColumn": 14, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 31, "column": 1, @@ -134,16 +114,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 41, - "column": 1, - "endLine": 41, - "endColumn": 33, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 42, "column": 1, @@ -174,16 +144,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 46, - "column": 1, - "endLine": 46, - "endColumn": 22, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 47, "column": 1, @@ -214,16 +174,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 51, - "column": 1, - "endLine": 51, - "endColumn": 31, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 52, "column": 1, @@ -254,16 +204,6 @@ "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", "severity": "ERROR" }, - { - "line": 56, - "column": 1, - "endLine": 56, - "endColumn": 27, - "problem": "InteropCallReflect", - "suggest": "", - "rule": "Reflect API usage is not allowed in interop calls when an \"Object\" parameter receives a class instance (arkts-interop-d2s-static-reflect-on-dynamic-instance)", - "severity": "ERROR" - }, { "line": 57, "column": 1, @@ -295,4 +235,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/object_literals_properties.ets b/ets2panda/linter/test/main/object_literals_properties.ets index 84c1d5db860e0f4e0d5f200b21e71e7b7b8741d0..1944ed2ef90d437ad280ef9b5227a5a026087bb3 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets +++ b/ets2panda/linter/test/main/object_literals_properties.ets @@ -264,22 +264,34 @@ namespace X { console.log("C - 1"); } } - + export interface I { m(a: number, b: string): void; } } - + function test() { let c: X.C = { m() { console.log("C - 2"); } } - + let i: X.I = { m(): void { console.log("I"); } } -} \ No newline at end of file +} + +class FooBarBaz { + foo?: Map + bar?: string +} + +function baz(fooBar: Map) { + baz2({fooBar}); +} + +function baz2(fooBarBaz: FooBarBaz) { +} diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.args.json b/ets2panda/linter/test/main/object_literals_properties.ets.args.json index f9fc1047e86a36642e6e262c41779a581f640a7f..0b296788ca915485ba49fbc6dc37c45e1c5a1cc8 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.args.json +++ b/ets2panda/linter/test/main/object_literals_properties.ets.args.json @@ -1,21 +1,21 @@ { - "copyright": [ - "Copyright (c) 2023-2025 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." - ], - "mode": { - "arkts2": "", - "autofix": "--arkts-2", - "migrate": "--arkts-2" - } + "copyright": [ + "Copyright (c) 2023-2025 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." + ], + "mode": { + "arkts2": "", + "autofix": "--arkts-2", + "migrate": "--arkts-2" + } } diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.arkts2.json b/ets2panda/linter/test/main/object_literals_properties.ets.arkts2.json index 4f42a0f593f239b67d468925e2630974976a6fc9..b72ff900ba7ccd4a3199f642e70b74ee16525763 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.arkts2.json +++ b/ets2panda/linter/test/main/object_literals_properties.ets.arkts2.json @@ -1,6 +1,6 @@ { "copyright": [ - "Copyright (c) 2023-2024 Huawei Device Co., Ltd.", + "Copyright (c) 2023-2025 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", @@ -1345,4 +1345,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.autofix.json b/ets2panda/linter/test/main/object_literals_properties.ets.autofix.json index 04eed5c40725e7ae3e575caa83ea8b14ab9a98fc..ccf6076bdc6c00243b5abe01469d556165d4bf23 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.autofix.json +++ b/ets2panda/linter/test/main/object_literals_properties.ets.autofix.json @@ -1,6 +1,6 @@ { "copyright": [ - "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Copyright (c) 2023-2025 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", @@ -2326,8 +2326,8 @@ "problem": "ObjectLiteralProperty", "autofix": [ { - "start": 4958, - "end": 4958, + "start": 4956, + "end": 4956, "replacementText": "class GeneratedObjectLiteralClass_11 extends X.C {\n m() {\n console.log(\"C - 2\");\n }\n}\n\n", "line": 275, "column": 5, @@ -2335,8 +2335,8 @@ "endColumn": 6 }, { - "start": 4991, - "end": 5040, + "start": 4989, + "end": 5038, "replacementText": "new GeneratedObjectLiteralClass_11()", "line": 275, "column": 5, @@ -2366,8 +2366,8 @@ "problem": "ObjectLiteralProperty", "autofix": [ { - "start": 4958, - "end": 4958, + "start": 4956, + "end": 4956, "replacementText": "class GeneratedObjectLiteralClass_12 implements X.I {\n m(): void {\n console.log(\"I\");\n }\n}\n\n", "line": 281, "column": 5, @@ -2375,8 +2375,8 @@ "endColumn": 6 }, { - "start": 5058, - "end": 5109, + "start": 5055, + "end": 5106, "replacementText": "new GeneratedObjectLiteralClass_12()", "line": 281, "column": 5, @@ -2399,4 +2399,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.migrate.ets b/ets2panda/linter/test/main/object_literals_properties.ets.migrate.ets index c2c24296503bfb2c9c06af0882c3e9dd3ea8e123..ef4628d62ad4102230155207f7d93a7427f70fdf 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.migrate.ets +++ b/ets2panda/linter/test/main/object_literals_properties.ets.migrate.ets @@ -326,12 +326,12 @@ namespace X { console.log("C - 1"); } } - + export interface I { m(a: number, b: string): void; } } - + class GeneratedObjectLiteralClass_11 extends X.C { m() { console.log("C - 2"); @@ -346,6 +346,18 @@ class GeneratedObjectLiteralClass_12 implements X.I { function test() { let c: X.C = new GeneratedObjectLiteralClass_11() - + let i: X.I = new GeneratedObjectLiteralClass_12() -} \ No newline at end of file +} + +class FooBarBaz { + foo?: Map + bar?: string +} + +function baz(fooBar: Map) { + baz2({fooBar}); +} + +function baz2(fooBarBaz: FooBarBaz) { +} diff --git a/ets2panda/linter/test/main/object_literals_properties.ets.migrate.json b/ets2panda/linter/test/main/object_literals_properties.ets.migrate.json index b652f8984b042e4ff3b80b0521adea79db14aef4..cb4957616222487070cf53339fedcabe2157fecb 100644 --- a/ets2panda/linter/test/main/object_literals_properties.ets.migrate.json +++ b/ets2panda/linter/test/main/object_literals_properties.ets.migrate.json @@ -1,6 +1,6 @@ { "copyright": [ - "Copyright (c) 2025 Huawei Device Co., Ltd.", + "Copyright (c) 2023-2025 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", @@ -335,4 +335,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +}