diff --git a/ets2panda/linter/rule-config.json b/ets2panda/linter/rule-config.json index 42df3797287831d8d1d132729a4dd602ef436bc3..3c8342c75bb5fa146bc9934903f892218300a602 100644 --- a/ets2panda/linter/rule-config.json +++ b/ets2panda/linter/rule-config.json @@ -14,6 +14,7 @@ "arkts-limited-stdlib", "arkts-no-classes-as-obj", "arkts-obj-literal-props", + "arkts-obj-literal-key-type", "arkts-optional-methods", "arkts-numeric-semantic", "arkts-incompatible-function-types", @@ -153,4 +154,4 @@ "arkts-limited-stdlib-no-concurrent-decorator", "arkts-no-need-stdlib-worker" ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/src/lib/CookBookMsg.ts b/ets2panda/linter/src/lib/CookBookMsg.ts index e1c85804dd8c93843b2095a13259952d555541d3..b46f143087afb31b9ba0ee3eaaa8a3c5e4134316 100644 --- a/ets2panda/linter/src/lib/CookBookMsg.ts +++ b/ets2panda/linter/src/lib/CookBookMsg.ts @@ -102,7 +102,7 @@ cookBookTag[71] = 'The comma operator "," is supported only in "for" loops (arkt cookBookTag[72] = ''; cookBookTag[73] = ''; cookBookTag[74] = 'Destructuring variable declarations are not supported (arkts-no-destruct-decls)'; -cookBookTag[75] = ''; +cookBookTag[75] = 'Use string-literal keys with Record (arkts-obj-literal-key-type)'; cookBookTag[76] = ''; cookBookTag[77] = ''; cookBookTag[78] = ''; diff --git a/ets2panda/linter/src/lib/FaultAttrs.ts b/ets2panda/linter/src/lib/FaultAttrs.ts index 14bc9b8f4b54ce9c112f6fbe835c7aee3974d213..9243904803e2781a3a182371f501c6f3a3e62770 100644 --- a/ets2panda/linter/src/lib/FaultAttrs.ts +++ b/ets2panda/linter/src/lib/FaultAttrs.ts @@ -64,6 +64,7 @@ faultsAttrs[FaultID.InOperator] = new FaultAttributes(66); faultsAttrs[FaultID.DestructuringAssignment] = new FaultAttributes(69); faultsAttrs[FaultID.CommaOperator] = new FaultAttributes(71); faultsAttrs[FaultID.DestructuringDeclaration] = new FaultAttributes(74); +faultsAttrs[FaultID.ObjectLiteralKeyType] = new FaultAttributes(75); faultsAttrs[FaultID.CatchWithUnsupportedType] = new FaultAttributes(79); faultsAttrs[FaultID.ForInStatement] = new FaultAttributes(80); faultsAttrs[FaultID.MappedType] = new FaultAttributes(83); diff --git a/ets2panda/linter/src/lib/FaultDesc.ts b/ets2panda/linter/src/lib/FaultDesc.ts index f55dc3b3a1c6518e62946dfd9f1277739f8729a8..35a349072319bdb52a77e836155d0e2d7d3dbb96 100644 --- a/ets2panda/linter/src/lib/FaultDesc.ts +++ b/ets2panda/linter/src/lib/FaultDesc.ts @@ -40,6 +40,7 @@ faultDesc[FaultID.InOperator] = '"in" operations'; faultDesc[FaultID.FunctionExpression] = 'function expressions'; faultDesc[FaultID.IntersectionType] = 'intersection types and type literals'; faultDesc[FaultID.ObjectTypeLiteral] = 'Object type literals'; +faultDesc[FaultID.ObjectLiteralKeyType] = 'Object literal key types'; faultDesc[FaultID.CommaOperator] = 'comma operator'; faultDesc[FaultID.LimitedReturnTypeInference] = 'Functions with limited return type inference'; faultDesc[FaultID.ClassExpression] = 'Class expressions'; diff --git a/ets2panda/linter/src/lib/Problems.ts b/ets2panda/linter/src/lib/Problems.ts index c39e5ac1c3e25636c2b6452cb46e353d006162c8..699c561b7d3421bad46acc48586432c538363a59 100644 --- a/ets2panda/linter/src/lib/Problems.ts +++ b/ets2panda/linter/src/lib/Problems.ts @@ -37,6 +37,7 @@ export enum FaultID { FunctionExpression, IntersectionType, ObjectTypeLiteral, + ObjectLiteralKeyType, CommaOperator, LimitedReturnTypeInference, ClassExpression, diff --git a/ets2panda/linter/src/lib/TypeScriptLinter.ts b/ets2panda/linter/src/lib/TypeScriptLinter.ts index 90024f48809f9e480207510eb1298cec7a954b93..5e6af4c6b7269578aba17e75f00aee8e403607c9 100644 --- a/ets2panda/linter/src/lib/TypeScriptLinter.ts +++ b/ets2panda/linter/src/lib/TypeScriptLinter.ts @@ -622,6 +622,10 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } const objectLiteralType = this.tsTypeChecker.getContextualType(objectLiteralExpr); + if (objectLiteralType && this.options.arkts2) { + this.isObjectLiteralKeyTypeValid(objectLiteralExpr, objectLiteralType); + } + if (objectLiteralType && this.tsUtils.typeContainsSendableClassOrInterface(objectLiteralType)) { this.incrementCounters(node, FaultID.SendableObjectInitialization); } else if ( @@ -2771,6 +2775,19 @@ export class TypeScriptLinter extends BaseTypeScriptLinter { } } + private isObjectLiteralKeyTypeValid(objectLiteral: ts.ObjectLiteralExpression, contextualType: ts.Type): void { + if (!this.tsUtils.isStdRecordType(contextualType)) { + return; + } + objectLiteral.properties.forEach((prop: ts.ObjectLiteralElementLike): void => { + if (ts.isPropertyAssignment(prop)) { + if (!this.tsUtils.isValidRecordObjectLiteralKey(prop.name)) { + this.incrementCounters(prop, FaultID.ObjectLiteralKeyType); + } + } + }); + } + private handleVariableDeclaration(node: ts.Node): void { const tsVarDecl = node as ts.VariableDeclaration; this.handleVariableDeclarationForProp(tsVarDecl); diff --git a/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets b/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets new file mode 100644 index 0000000000000000000000000000000000000000..0079ae69de97af89494f2ec7d79b3c9e3e79ab39 --- /dev/null +++ b/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets @@ -0,0 +1,38 @@ +/* + * Copyright (c) 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. + */ + + +class A{ + public age:number = 1.0; +} + +class B{ + public name:string = "Joe"; + public age:number = 30; +} + +class C{ + public salary:number = 100; + public age:number = 30; +} + +let a: A = {"age": 30} + +let b: Record = { age: 30} + +let c: B = {"name" : "Annie", "age" : 25} + +let d: Record = {salary : 250, age: 30} + diff --git a/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets.args.json b/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets.args.json new file mode 100644 index 0000000000000000000000000000000000000000..bc4d2071daf6e9354e711c3b74b6be2b56659066 --- /dev/null +++ b/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets.args.json @@ -0,0 +1,19 @@ +{ + "copyright": [ + "Copyright (c) 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": "" + } +} diff --git a/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets.arkts2.json b/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets.arkts2.json new file mode 100644 index 0000000000000000000000000000000000000000..d9865e34233a35209039277b727b8e0a08548d9f --- /dev/null +++ b/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets.arkts2.json @@ -0,0 +1,178 @@ +{ + "copyright": [ + "Copyright (c) 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." + ], + "result": [ + { + "line": 23, + "column": 25, + "endLine": 23, + "endColumn": 27, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 27, + "column": 28, + "endLine": 27, + "endColumn": 31, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 28, + "column": 25, + "endLine": 28, + "endColumn": 27, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 31, + "column": 13, + "endLine": 31, + "endColumn": 18, + "problem": "LiteralAsPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)", + "severity": "ERROR" + }, + { + "line": 31, + "column": 20, + "endLine": 31, + "endColumn": 22, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 33, + "column": 35, + "endLine": 33, + "endColumn": 42, + "problem": "ObjectLiteralKeyType", + "suggest": "", + "rule": "Use string-literal keys with Record (arkts-obj-literal-key-type)", + "severity": "ERROR" + }, + { + "line": 33, + "column": 33, + "endLine": 33, + "endColumn": 34, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + }, + { + "line": 33, + "column": 40, + "endLine": 33, + "endColumn": 42, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 35, + "column": 13, + "endLine": 35, + "endColumn": 19, + "problem": "LiteralAsPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)", + "severity": "ERROR" + }, + { + "line": 35, + "column": 31, + "endLine": 35, + "endColumn": 36, + "problem": "LiteralAsPropertyName", + "suggest": "", + "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)", + "severity": "ERROR" + }, + { + "line": 35, + "column": 39, + "endLine": 35, + "endColumn": 41, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 37, + "column": 34, + "endLine": 37, + "endColumn": 46, + "problem": "ObjectLiteralKeyType", + "suggest": "", + "rule": "Use string-literal keys with Record (arkts-obj-literal-key-type)", + "severity": "ERROR" + }, + { + "line": 37, + "column": 48, + "endLine": 37, + "endColumn": 55, + "problem": "ObjectLiteralKeyType", + "suggest": "", + "rule": "Use string-literal keys with Record (arkts-obj-literal-key-type)", + "severity": "ERROR" + }, + { + "line": 37, + "column": 33, + "endLine": 37, + "endColumn": 34, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + }, + { + "line": 37, + "column": 43, + "endLine": 37, + "endColumn": 46, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + }, + { + "line": 37, + "column": 53, + "endLine": 37, + "endColumn": 55, + "problem": "NumericSemantics", + "suggest": "", + "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", + "severity": "ERROR" + } + ] +} diff --git a/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets.json b/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets.json new file mode 100644 index 0000000000000000000000000000000000000000..c41e2343989fdaf2f20b1c92f4c941cf0fa1b469 --- /dev/null +++ b/ets2panda/linter/test/main/arkts-obj-literal-key-type.ets.json @@ -0,0 +1,38 @@ +{ + "copyright": [ + "Copyright (c) 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." + ], + "result": [ + { + "line": 33, + "column": 33, + "endLine": 33, + "endColumn": 34, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + }, + { + "line": 37, + "column": 33, + "endLine": 37, + "endColumn": 34, + "problem": "ObjectLiteralNoContextType", + "suggest": "", + "rule": "Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals)", + "severity": "ERROR" + } + ] +} diff --git a/ets2panda/linter/test/main/literals_as_prop_names.ets.arkts2.json b/ets2panda/linter/test/main/literals_as_prop_names.ets.arkts2.json index a1c3a4b9724091615941ad405ba2ef2ceaeaaf5c..25a3ca26f62f9baab6dbf8cc743387bf9b008a95 100644 --- a/ets2panda/linter/test/main/literals_as_prop_names.ets.arkts2.json +++ b/ets2panda/linter/test/main/literals_as_prop_names.ets.arkts2.json @@ -554,6 +554,16 @@ "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", "severity": "ERROR" }, + { + "line": 136, + "column": 3, + "endLine": 136, + "endColumn": 15, + "problem": "ObjectLiteralKeyType", + "suggest": "", + "rule": "Use string-literal keys with Record (arkts-obj-literal-key-type)", + "severity": "ERROR" + }, { "line": 135, "column": 37, @@ -805,4 +815,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/literals_as_prop_names.ets.autofix.json b/ets2panda/linter/test/main/literals_as_prop_names.ets.autofix.json index 5d84566f2e6bf018a371494965a41f7db4df0c5a..4080489730943fccdb7a751831c55b40b84dec3c 100644 --- a/ets2panda/linter/test/main/literals_as_prop_names.ets.autofix.json +++ b/ets2panda/linter/test/main/literals_as_prop_names.ets.autofix.json @@ -990,6 +990,16 @@ "rule": "Numeric semantics is different for integer values (arkts-numeric-semantic)", "severity": "ERROR" }, + { + "line": 136, + "column": 3, + "endLine": 136, + "endColumn": 15, + "problem": "ObjectLiteralKeyType", + "suggest": "", + "rule": "Use string-literal keys with Record (arkts-obj-literal-key-type)", + "severity": "ERROR" + }, { "line": 135, "column": 37, @@ -1351,4 +1361,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/literals_as_prop_names.ets.migrate.json b/ets2panda/linter/test/main/literals_as_prop_names.ets.migrate.json index 40d660f102f671807bdb1a196838505d64ef1f6b..3b5d627860fa5ce150f836bb8f54f8e8cd5e0c38 100644 --- a/ets2panda/linter/test/main/literals_as_prop_names.ets.migrate.json +++ b/ets2panda/linter/test/main/literals_as_prop_names.ets.migrate.json @@ -224,6 +224,16 @@ "rule": "Objects with property names that are not identifiers are not supported (arkts-identifiers-as-prop-names)", "severity": "ERROR" }, + { + "line": 145, + "column": 3, + "endLine": 145, + "endColumn": 17, + "problem": "ObjectLiteralKeyType", + "suggest": "", + "rule": "Use string-literal keys with Record (arkts-obj-literal-key-type)", + "severity": "ERROR" + }, { "line": 144, "column": 37, @@ -385,4 +395,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/numeric_semantics2.ets.arkts2.json b/ets2panda/linter/test/main/numeric_semantics2.ets.arkts2.json index 8c2a778ec9f98e3debce809d5fc606ab7aa784bc..afbbb893dfd27386d5c9c87f365feaeccc52296f 100755 --- a/ets2panda/linter/test/main/numeric_semantics2.ets.arkts2.json +++ b/ets2panda/linter/test/main/numeric_semantics2.ets.arkts2.json @@ -715,4 +715,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/numeric_semantics2.ets.autofix.json b/ets2panda/linter/test/main/numeric_semantics2.ets.autofix.json index 9cee3116485dd985f550b19ad95c9d6f09e67846..5bc0e91eaca72ee8d21dd230b035f3266df7b1db 100644 --- a/ets2panda/linter/test/main/numeric_semantics2.ets.autofix.json +++ b/ets2panda/linter/test/main/numeric_semantics2.ets.autofix.json @@ -1253,4 +1253,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +} diff --git a/ets2panda/linter/test/main/numeric_semantics2.ets.migrate.json b/ets2panda/linter/test/main/numeric_semantics2.ets.migrate.json index 2103eb7cf5387f30295dca218d7ff81e6a850e86..3f86f1a88b08a4e9f8c826153a26827a9083cf8b 100644 --- a/ets2panda/linter/test/main/numeric_semantics2.ets.migrate.json +++ b/ets2panda/linter/test/main/numeric_semantics2.ets.migrate.json @@ -275,4 +275,4 @@ "severity": "ERROR" } ] -} \ No newline at end of file +}