From 9a09e777d0cd473492d564280d64ee54d967cc14 Mon Sep 17 00:00:00 2001 From: xingshunxiang Date: Wed, 30 Jul 2025 15:18:08 +0800 Subject: [PATCH] CTE when skip required prop in object literal Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICPT3O?from=project-issue Description: throw CTE for skipping non-optional properties in object literal Reason: according to spec 7.5.2, Properties of a non-optional type cannot be skipped in an object literal, despite some property types having default values Tests: ninja tests passed tests/tests-u-runner/runner.sh --ets-cts --show-progress --build-dir x64.release --processes=all passed tests/tests-u-runner/runner.sh --ets-func-tests --show-progress --build-dir x64.release --processes=all passed tests/tests-u-runner/runner.sh --astchecker --show-progress --build-dir x64.release --processes=all passed tests/tests-u-runner/runner.sh --ets-runtime --show-progress --build-dir x64.release --processes=all passed tests/tests-u-runner/runner.sh --parser --no-js --show-progress --build-dir x64.release --processes=all passed Signed-off-by: xingshunxiang --- ets2panda/checker/ETSAnalyzer.cpp | 62 +++++++++++++++++-- ets2panda/checker/ETSAnalyzer.h | 5 ++ ets2panda/scripts/arkui.properties | 2 +- ...ace_object_literal_missing_assignment0.ets | 24 +++++++ ...ace_object_literal_missing_assignment1.ets | 29 +++++++++ ...ace_object_literal_missing_assignment2.ets | 36 +++++++++++ ...eadExpressionAsPropertyInObjectLiteral.ets | 5 +- .../ets/FixedArray/record_object_value.ets | 14 +++-- .../ast/parser/ets/record_object_value.ets | 14 +++-- .../parser/ets/required_multiple_fields.ets | 3 + ets2panda/util/diagnostic/semantic.yaml | 8 +++ 11 files changed, 184 insertions(+), 18 deletions(-) create mode 100644 ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment0.ets create mode 100644 ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment1.ets create mode 100644 ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment2.ets diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 1974f525ef..03014cc580 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -2355,13 +2355,40 @@ checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const return objType; } -void ETSAnalyzer::CheckObjectExprProps(const ir::ObjectExpression *expr, - checker::ETSObjectType *objectTypeForProperties, - checker::PropertySearchFlags searchFlags) const +void ETSAnalyzer::CollectNonOptionalProperty(const ETSObjectType *objType, + std::unordered_map &props) const { ETSChecker *checker = GetETSChecker(); - checker::ETSObjectType *objType = objectTypeForProperties; + // Note: all the properties of an interface will be lowered as accessor before checker. + auto const &methodMap = objType->InstanceMethods(); + for (const auto &[propName, var] : methodMap) { + if (!checker->IsVariableGetterSetter(var)) { + continue; + } + + auto propertyType = checker->GetTypeOfVariable(var); + if (propertyType->IsTypeError()) { + // Note: error handle later. + continue; + } + if (checker->Relation()->IsSupertypeOf(propertyType, checker->GlobalETSUndefinedType())) { + // non-optional properties + continue; + } + props.insert({propName, const_cast(objType)}); + } + + for (auto const *superInterface : objType->Interfaces()) { + CollectNonOptionalProperty(superInterface, props); + } +} + +void ETSAnalyzer::CheckObjectExprPropsHelper(const ir::ObjectExpression *expr, checker::ETSObjectType *objType, + checker::PropertySearchFlags searchFlags, + std::unordered_map &properties) const +{ + ETSChecker *checker = GetETSChecker(); for (ir::Expression *propExpr : expr->Properties()) { if (!propExpr->IsProperty()) { checker->LogError(diagnostic::OBJECT_LITERAL_NOT_KV, {}, expr->Start()); @@ -2403,6 +2430,33 @@ void ETSAnalyzer::CheckObjectExprProps(const ir::ObjectExpression *expr, checker::AssignmentContext(checker->Relation(), value, value->TsType(), propType, value->Start(), {{diagnostic::PROP_INCOMPAT, {value->TsType(), propType, pname}}}); + if (properties.find(pname) != properties.end()) { + properties.erase(pname); + } + } +} + +void ETSAnalyzer::CheckObjectExprProps(const ir::ObjectExpression *expr, + checker::ETSObjectType *objectTypeForProperties, + checker::PropertySearchFlags searchFlags) const +{ + ETSChecker *checker = GetETSChecker(); + checker::ETSObjectType *objType = objectTypeForProperties; + + std::unordered_map propertyWithNonOptionalType; + if (objType->HasObjectFlag(ETSObjectFlags::INTERFACE)) { + CollectNonOptionalProperty(objType, propertyWithNonOptionalType); + } + + CheckObjectExprPropsHelper(expr, objType, searchFlags, propertyWithNonOptionalType); + + for (const auto &[propName, ownerType] : propertyWithNonOptionalType) { + if (objType == ownerType) { + checker->LogError(diagnostic::OBJECT_LITERAL_NON_OPTIONAL_PROP_LOST, {propName, objType}, expr->Start()); + } else { + checker->LogError(diagnostic::OBJECT_LITERAL_NON_OPTIONAL_PROP_OF_SUPER_LOST, + {propName, ownerType, objType}, expr->Start()); + } } if (objType->HasObjectFlag(ETSObjectFlags::REQUIRED)) { diff --git a/ets2panda/checker/ETSAnalyzer.h b/ets2panda/checker/ETSAnalyzer.h index c9a398b319..96782fb189 100644 --- a/ets2panda/checker/ETSAnalyzer.h +++ b/ets2panda/checker/ETSAnalyzer.h @@ -40,6 +40,11 @@ public: checker::Type *CheckDynamic(ir::ObjectExpression *expr) const; checker::Type *GetPreferredType(ir::ArrayExpression *expr) const; void GetUnionPreferredType(ir::Expression *expr, Type *originalType) const; + void CollectNonOptionalProperty(const ETSObjectType *objType, + std::unordered_map &props) const; + void CheckObjectExprPropsHelper(const ir::ObjectExpression *expr, checker::ETSObjectType *objType, + checker::PropertySearchFlags searchFlags, + std::unordered_map &properties) const; void CheckObjectExprProps(const ir::ObjectExpression *expr, checker::ETSObjectType *objectTypeForProperties, checker::PropertySearchFlags searchFlags) const; std::tuple CheckAssignmentExprOperatorType(ir::AssignmentExpression *expr, diff --git a/ets2panda/scripts/arkui.properties b/ets2panda/scripts/arkui.properties index 54df76e008..81cb4b5794 100644 --- a/ets2panda/scripts/arkui.properties +++ b/ets2panda/scripts/arkui.properties @@ -1,3 +1,3 @@ ARKUI_DEV_REPO=https://gitee.com/rri_opensource/koala_projects.git -ARKUI_DEV_BRANCH=panda_rev_8-remove-primitives +ARKUI_DEV_BRANCH=panda_rev_10-fix-objectliteral ARKUI_DEST=koala-sig diff --git a/ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment0.ets b/ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment0.ets new file mode 100644 index 0000000000..fb08582a04 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment0.ets @@ -0,0 +1,24 @@ +/* + * 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. + */ + +interface A{ + readonly name: string; +} + +function createA(): A{ + return /* @@ label */{}; +} + +/* @@@ label Error TypeError: Non-optional property 'name' in type 'A' is missing in object literal. */ diff --git a/ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment1.ets b/ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment1.ets new file mode 100644 index 0000000000..3dc305d1ea --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment1.ets @@ -0,0 +1,29 @@ +/* + * 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 {} + +interface X { + id: string; + nm: number; + a: A; +} + +function foo(x: X) {} +foo(/* @@ label */{}) + +/* @@@ label Error TypeError: Non-optional property 'a' in type 'X' is missing in object literal. */ +/* @@@ label Error TypeError: Non-optional property 'nm' in type 'X' is missing in object literal. */ +/* @@@ label Error TypeError: Non-optional property 'id' in type 'X' is missing in object literal. */ diff --git a/ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment2.ets b/ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment2.ets new file mode 100644 index 0000000000..ffe940d1f5 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/interface_object_literal_missing_assignment2.ets @@ -0,0 +1,36 @@ +/* + * 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 {} + +interface Y { + b: int; +} + +interface Z { + c: int; +} + +interface X extends Y, Z { + id?: string; + nm?: number; + a?: A; +} + +function foo(x: X) {} +foo(/* @@ label */{}) + +/* @@@ label Error TypeError: Non-optional property 'c' in super type 'Z' of type 'X' is missing in object literal. */ +/* @@@ label Error TypeError: Non-optional property 'b' in super type 'Y' of type 'X' is missing in object literal. */ diff --git a/ets2panda/test/ast/compiler/ets/spreadExpressionAsPropertyInObjectLiteral.ets b/ets2panda/test/ast/compiler/ets/spreadExpressionAsPropertyInObjectLiteral.ets index bef75565fa..a73d747d90 100644 --- a/ets2panda/test/ast/compiler/ets/spreadExpressionAsPropertyInObjectLiteral.ets +++ b/ets2panda/test/ast/compiler/ets/spreadExpressionAsPropertyInObjectLiteral.ets @@ -21,4 +21,7 @@ const b2: Base = /* @@ label */{ ...{n: 200} } const c1: Child = /* @@ label1 */{ ...b1, a: "a" } /* @@@ label Error TypeError: The object literal properties must be key-value pairs */ -/* @@@ label1 Error TypeError: The object literal properties must be key-value pairs */ \ No newline at end of file +/* @@@ label Error TypeError: Non-optional property 'n' in type 'Base' is missing in object literal. */ +/* @@@ label1 Error TypeError: The object literal properties must be key-value pairs */ +/* @@@ label1 Error TypeError: Non-optional property 'n' in super type 'Base' of type 'Child' is missing in object literal. */ +/* @@@ label1 Error TypeError: Non-optional property 'a' in type 'Child' is missing in object literal. */ diff --git a/ets2panda/test/ast/parser/ets/FixedArray/record_object_value.ets b/ets2panda/test/ast/parser/ets/FixedArray/record_object_value.ets index 90442a1300..499e9e0eab 100644 --- a/ets2panda/test/ast/parser/ets/FixedArray/record_object_value.ets +++ b/ets2panda/test/ast/parser/ets/FixedArray/record_object_value.ets @@ -48,8 +48,8 @@ function main(){ }; let errormap2:Record = { - "john":{/* @@ label2 */agee:10, salary:10}, - "Mary":{age:21, salary:10, /* @@ label3 */other:10} + "john":/* @@ label2 */{/* @@ label3 */agee:10, salary:10}, + "Mary":{age:21, salary:10, /* @@ label4 */other:10} }; let stringarraymap:Record> = { @@ -57,7 +57,9 @@ function main(){ "Mary":["20", "30"] }; } -/* @@@ label Error TypeError: Type '"10"' is not compatible with type 'double' at property 'age' */ -/* @@@ label1 Error TypeError: Type '"100"' is not compatible with type 'double' at property 'salary' */ -/* @@@ label2 Error TypeError: type PersonInfoInterface has no property named agee */ -/* @@@ label3 Error TypeError: type PersonInfoInterface has no property named other */ +/* @@@ label Error TypeError: Type '"10"' is not compatible with type 'double' at property 'age' */ +/* @@@ label1 Error TypeError: Type '"100"' is not compatible with type 'double' at property 'salary' */ +/* @@@ label2 Error TypeError: Non-optional property 'salary' in type 'PersonInfoInterface' is missing in object literal. */ +/* @@@ label2 Error TypeError: Non-optional property 'age' in type 'PersonInfoInterface' is missing in object literal. */ +/* @@@ label3 Error TypeError: type PersonInfoInterface has no property named agee */ +/* @@@ label4 Error TypeError: type PersonInfoInterface has no property named other */ diff --git a/ets2panda/test/ast/parser/ets/record_object_value.ets b/ets2panda/test/ast/parser/ets/record_object_value.ets index c691052402..cf9a67214a 100644 --- a/ets2panda/test/ast/parser/ets/record_object_value.ets +++ b/ets2panda/test/ast/parser/ets/record_object_value.ets @@ -48,8 +48,8 @@ function main(){ }; let errormap2:Record = { - "john":{/* @@ label2 */agee:10, salary:10}, - "Mary":{age:21, salary:10, /* @@ label3 */other:10} + "john":/* @@ label2 */{/* @@ label3 */agee:10, salary:10}, + "Mary":{age:21, salary:10, /* @@ label4 */other:10} }; let stringarraymap:Record = { @@ -57,7 +57,9 @@ function main(){ "Mary":["20", "30"] }; } -/* @@@ label Error TypeError: Type '"10"' is not compatible with type 'double' at property 'age' */ -/* @@@ label1 Error TypeError: Type '"100"' is not compatible with type 'double' at property 'salary' */ -/* @@@ label2 Error TypeError: type PersonInfoInterface has no property named agee */ -/* @@@ label3 Error TypeError: type PersonInfoInterface has no property named other */ +/* @@@ label Error TypeError: Type '"10"' is not compatible with type 'double' at property 'age' */ +/* @@@ label1 Error TypeError: Type '"100"' is not compatible with type 'double' at property 'salary' */ +/* @@@ label2 Error TypeError: Non-optional property 'salary' in type 'PersonInfoInterface' is missing in object literal. */ +/* @@@ label2 Error TypeError: Non-optional property 'age' in type 'PersonInfoInterface' is missing in object literal. */ +/* @@@ label3 Error TypeError: type PersonInfoInterface has no property named agee */ +/* @@@ label4 Error TypeError: type PersonInfoInterface has no property named other */ diff --git a/ets2panda/test/ast/parser/ets/required_multiple_fields.ets b/ets2panda/test/ast/parser/ets/required_multiple_fields.ets index 4f68534936..4b509c3fc1 100644 --- a/ets2panda/test/ast/parser/ets/required_multiple_fields.ets +++ b/ets2panda/test/ast/parser/ets/required_multiple_fields.ets @@ -35,6 +35,9 @@ function main() { /* @@@ label1 Error TypeError: Class property 'k' needs to be initialized for Required. */ /* @@@ label2 Error TypeError: Class property 'j' needs to be initialized for Required. */ /* @@@ label2 Error TypeError: Class property 'k' needs to be initialized for Required. */ +/* @@@ label3 Error TypeError: Non-optional property 'k' in type 'Required' is missing in object literal. */ /* @@@ label3 Error TypeError: Class property 'k' needs to be initialized for Required. */ +/* @@@ label4 Error TypeError: Non-optional property 'j' in type 'Required' is missing in object literal. */ +/* @@@ label4 Error TypeError: Non-optional property 'k' in type 'Required' is missing in object literal. */ /* @@@ label4 Error TypeError: Class property 'j' needs to be initialized for Required. */ /* @@@ label4 Error TypeError: Class property 'k' needs to be initialized for Required. */ diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index 9a6cf73a51..c9030ea969 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -1543,3 +1543,11 @@ semantic: - name: TYPEOF_IN_ANNOTATION id: 395 message: "'typeof' is not allowed in type annotations." + +- name: OBJECT_LITERAL_NON_OPTIONAL_PROP_LOST + id: 400 + message: "Non-optional property '{}' in type '{}' is missing in object literal." + +- name: OBJECT_LITERAL_NON_OPTIONAL_PROP_OF_SUPER_LOST + id: 401 + message: "Non-optional property '{}' in super type '{}' of type '{}' is missing in object literal." \ No newline at end of file -- Gitee