diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 1974f525ef8f53cb2a499789783464e0beb6c932..03014cc580470d4f17b8e7dc91d19bd3c8437823 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 c9a398b319b807b28c697498308e787f9103af69..96782fb189354e61d11411add1ecf9353308315e 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 54df76e0089e1aea4d19c22fd97c790d6066dd40..81cb4b5794e071332e7d2f3a802d9f136e3031ec 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 0000000000000000000000000000000000000000..fb08582a043505cf12a14b95739f748eb276ae25 --- /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 0000000000000000000000000000000000000000..3dc305d1ea201da9dc2b75be86d59045fd842c20 --- /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 0000000000000000000000000000000000000000..ffe940d1f51460f1ff0d27041ac2037f1d2163cc --- /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 bef75565fae3580a50411132d71ceadda5e07bd5..a73d747d9053d6962e7569c8e79754138772ae3c 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 90442a1300b850ec2513c1751658e4279d8c68a4..499e9e0eaba7ab139231f4d950dc0bf52264df0f 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 c691052402fa30d5e77b23b9ffda3b128e746831..cf9a67214a2c9da32eb2cecae4961f174a789948 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 4f68534936f6b404b20392eb61eb942f3f66debb..4b509c3fc17962c5ad0a61db92f5cf3ea207c88a 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 9a6cf73a510e2eb83ec331f7a7c9cc1108ae299d..c9030ea969b92717885cc17e90dc1a4dc9e1eacf 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