diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 4748735b14712406ac39fcfd00a2bbfc502f3660..7c2edff7e0be2f127766b8700cc49d3a2b66606b 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -1218,7 +1218,7 @@ checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const } if (expr->target_ != nullptr && !expr->IsIgnoreConstAssign()) { - checker->ValidateUnaryOperatorOperand(expr->target_); + checker->ValidateUnaryOperatorOperand(expr->target_, expr); } auto [rightType, relationNode] = CheckAssignmentExprOperatorType(expr, leftType); @@ -2764,20 +2764,20 @@ checker::Type *ETSAnalyzer::Check(ir::UpdateExpression *expr) const } if (expr->Argument()->IsIdentifier()) { - checker->ValidateUnaryOperatorOperand(expr->Argument()->AsIdentifier()->Variable()); + checker->ValidateUnaryOperatorOperand(expr->Argument()->AsIdentifier()->Variable(), expr); } else if (expr->Argument()->IsTSAsExpression()) { if (auto *const asExprVar = expr->Argument()->AsTSAsExpression()->Variable(); asExprVar != nullptr) { - checker->ValidateUnaryOperatorOperand(asExprVar); + checker->ValidateUnaryOperatorOperand(asExprVar, expr); } } else if (expr->Argument()->IsTSNonNullExpression()) { if (auto *const nonNullExprVar = expr->Argument()->AsTSNonNullExpression()->Variable(); nonNullExprVar != nullptr) { - checker->ValidateUnaryOperatorOperand(nonNullExprVar); + checker->ValidateUnaryOperatorOperand(nonNullExprVar, expr); } } else if (expr->Argument()->IsMemberExpression()) { varbinder::LocalVariable *propVar = expr->argument_->AsMemberExpression()->PropVar(); if (propVar != nullptr) { - checker->ValidateUnaryOperatorOperand(propVar); + checker->ValidateUnaryOperatorOperand(propVar, expr); } } else { ES2PANDA_ASSERT(checker->IsAnyError()); diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 40549ea10234e95a65448bd2e1935cb0d0022004..c8e6bae7da271323516b29118d36f51b3f0cc823 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -620,7 +620,7 @@ public: const checker::Type *GetElementTypeOfArray(const checker::Type *type) const; bool IsNullLikeOrVoidExpression(const ir::Expression *expr) const; bool IsConstantExpression(ir::Expression *expr, Type *type); - void ValidateUnaryOperatorOperand(varbinder::Variable *variable); + void ValidateUnaryOperatorOperand(varbinder::Variable *variable, ir::Expression *expr); void CheckFunctionSignatureAnnotations(const ArenaVector ¶ms, ir::TSTypeParameterDeclaration *typeParams, ir::TypeNode *returnTypeAnnotation); diff --git a/ets2panda/checker/ets/validateHelpers.cpp b/ets2panda/checker/ets/validateHelpers.cpp index feee4a04aa7381f7c5d899f5d5b3168f17f9547a..11043e0dee7ee16daff0ae96ebfd29f0773e0ea0 100644 --- a/ets2panda/checker/ets/validateHelpers.cpp +++ b/ets2panda/checker/ets/validateHelpers.cpp @@ -203,7 +203,7 @@ void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident) } } -void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable) +void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable, ir::Expression *expr) { if (IsVariableGetterSetter(variable)) { return; @@ -221,7 +221,7 @@ void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable) } if (!HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK)) { std::ignore = TypeError(variable, diagnostic::FIELD_ASSIGN_TYPE_MISMATCH, {fieldType, variable->Name()}, - variable->Declaration()->Node()->Start()); + expr->Start()); } if (variable->HasFlag(varbinder::VariableFlags::INIT_IN_STATIC_BLOCK)) { diff --git a/ets2panda/compiler/lowering/ets/constantExpressionLowering.cpp b/ets2panda/compiler/lowering/ets/constantExpressionLowering.cpp index 6a08c0793267ff64310552dba8c2f47a6080a596..eaf664e55e750af9d9bb690bdd37d32f6a11b96d 100644 --- a/ets2panda/compiler/lowering/ets/constantExpressionLowering.cpp +++ b/ets2panda/compiler/lowering/ets/constantExpressionLowering.cpp @@ -1280,6 +1280,15 @@ ir::AstNode *ConstantExpressionLowering::MaybeUnfoldIdentifier(ir::Identifier *n return node; } + // Left-Hand-Side identifiers in UpdateExpression or BinaryExpression cannot be unfolded + if (node->Parent()->IsUpdateExpression() && node->Parent()->AsUpdateExpression()->Argument() == node) { + return node; + } + + if (node->Parent()->IsAssignmentExpression() && node->Parent()->AsAssignmentExpression()->Left() == node) { + return node; + } + auto *resolved = ResolveIdentifier(node); if (resolved == nullptr || !(resolved->Declaration()->IsConstDecl() || resolved->Declaration()->IsReadonlyDecl())) { return node; diff --git a/ets2panda/lsp/src/register_code_fix/convert_const_to_let.cpp b/ets2panda/lsp/src/register_code_fix/convert_const_to_let.cpp index 057b14b581911f4b3ca033186828069fce4379dd..4d91d17813b7123cbe56596d304de48cffa9092c 100644 --- a/ets2panda/lsp/src/register_code_fix/convert_const_to_let.cpp +++ b/ets2panda/lsp/src/register_code_fix/convert_const_to_let.cpp @@ -33,12 +33,12 @@ void FixConvertConstToLet::MakeChangeForConvertConstToLet(ChangeTracker &changeT size_t pos) { auto *token = GetTouchingToken(context, pos, false); - if (token == nullptr || !token->IsNumberLiteral() || token->OriginalNode() == nullptr) { + if (token == nullptr) { return; } auto *scope = compiler::NearestScope(token); - auto *resolvedDecl = FindDeclInScopeWithFallback(scope, token->OriginalNode()->AsIdentifier()->Name()); + auto *resolvedDecl = FindDeclInScopeWithFallback(scope, token->AsIdentifier()->Name()); if (resolvedDecl == nullptr) { return; } diff --git a/ets2panda/test/ast/compiler/ets/assign_const.ets b/ets2panda/test/ast/compiler/ets/assign_const.ets new file mode 100644 index 0000000000000000000000000000000000000000..150e93078fba6d138db5456d9106038e3519a10a --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/assign_const.ets @@ -0,0 +1,21 @@ +/* + * 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. + */ + +function main(): void { + const c = 0; + /* @@ label */c = c + 1; +} + +/* @@@ label Error TypeError: Cannot assign to a constant variable c */ diff --git a/ets2panda/test/ast/compiler/ets/package_invalid_initializer/P3/P3.ets b/ets2panda/test/ast/compiler/ets/package_invalid_initializer/P3/P3.ets index 809e03282c8d1daf2580a62df3f0c732f27b23b3..3609cbb822433097e51adf6a68531a869e015874 100644 --- a/ets2panda/test/ast/compiler/ets/package_invalid_initializer/P3/P3.ets +++ b/ets2panda/test/ast/compiler/ets/package_invalid_initializer/P3/P3.ets @@ -47,8 +47,8 @@ static { /* @@? P3.ets:29:14 Error SyntaxError: Missing initialization for const package property */ /* @@? P3.ets:29:18 Error SyntaxError: Variable must be initialized or it's type must be declared. */ /* @@? P3.ets:30:30 Error SyntaxError: Non-constant initializer of Package should be apply in Initializer Block. */ +/* @@? P3.ets:31:14 Error TypeError: Cannot reassign constant c_nn2 */ /* @@? P3.ets:32:6 Error SyntaxError: Non-constant initializer of Package should be apply in Initializer Block. */ /* @@? P3.ets:33:1 Error SyntaxError: Invalid package toplevel statement */ /* @@? P3.ets:34:1 Error SyntaxError: Invalid package toplevel statement */ -/* @@? P3.ets:36:5 Error TypeError: Invalid left-hand side of assignment expression */ /* @@? P3.ets:39:14 Error SyntaxError: Missing initialization for const package property */ diff --git a/ets2panda/test/ast/compiler/ets/readonlyField.ets b/ets2panda/test/ast/compiler/ets/readonlyField.ets index 808ae692ee6df8f203cddae6d3f1fa9536ddfcfc..4b48970029505fe74164a27e30ec7937146c82fe 100644 --- a/ets2panda/test/ast/compiler/ets/readonlyField.ets +++ b/ets2panda/test/ast/compiler/ets/readonlyField.ets @@ -13,7 +13,7 @@ * limitations under the License. */ class ReadonlyData { - readonly /* @@ label */value: T; + readonly value: T; constructor(value: T) { this.value = value; } @@ -21,7 +21,7 @@ class ReadonlyData { function main(): void { let myData = new ReadonlyData("import data"); - myData.value = "new data"; + /* @@ label */myData.value = "new data"; } /* @@@ label Error TypeError: Cannot assign to a readonly variable value */ diff --git a/ets2panda/test/ast/compiler/ets/readonlyField_2.ets b/ets2panda/test/ast/compiler/ets/readonlyField_2.ets index 96451387facf12e946eceb951b435e1a530bd4d9..9dc39f7fa44be247318d885cf06cc13fc2b8ea0a 100644 --- a/ets2panda/test/ast/compiler/ets/readonlyField_2.ets +++ b/ets2panda/test/ast/compiler/ets/readonlyField_2.ets @@ -14,12 +14,12 @@ */ class ReadonlyPerson { - readonly /* @@ label */name: string; + readonly name: string; readonly age: number; } function updatePerson(person: ReadonlyPerson): ReadonlyPerson { - person.name = "Bob"; //CTE + /* @@ label */person.name = "Bob"; //CTE return person; } function main(): void { diff --git a/ets2panda/test/ast/compiler/ets/readonlyType_1.ets b/ets2panda/test/ast/compiler/ets/readonlyType_1.ets index 8a677d6521e06a57899f2117b5776990490dbb97..b2680473fc15c6ad0c57183fb2b8bed3f2396c2c 100644 --- a/ets2panda/test/ast/compiler/ets/readonlyType_1.ets +++ b/ets2panda/test/ast/compiler/ets/readonlyType_1.ets @@ -14,7 +14,7 @@ */ class A { - /* @@ label */fld: Number + fld: Number constructor (fld : Number) { this.fld = fld } @@ -22,9 +22,9 @@ class A { function foo(a0: A) { let a: Readonly = new A(2) - /* @@ label2 */a.fld = 5 + /* @@ label */a.fld = 5 } /* @@@ label Error TypeError: Cannot assign to a readonly variable fld */ -/* @@@ label2 Error TypeError: The 'Readonly' property cannot be reassigned. */ +/* @@@ label Error TypeError: The 'Readonly' property cannot be reassigned. */ diff --git a/ets2panda/test/ast/compiler/ets/readonlyType_2.ets b/ets2panda/test/ast/compiler/ets/readonlyType_2.ets index 17d1eaf99c29a370fbcc440273606bed37a10746..5685a9042ef15463186ccfd86e9a53e3c3199a9e 100644 --- a/ets2panda/test/ast/compiler/ets/readonlyType_2.ets +++ b/ets2panda/test/ast/compiler/ets/readonlyType_2.ets @@ -14,13 +14,13 @@ */ class A { - /* @@ label */fld: Number = 2 + fld: Number = 2 } function foo(a0: A) { let a: Readonly = {fld: 3} - /* @@ label2 */a.fld = 5 + /* @@ label */a.fld = 5 } /* @@@ label Error TypeError: Cannot assign to a readonly variable fld */ -/* @@@ label2 Error TypeError: The 'Readonly' property cannot be reassigned. */ +/* @@@ label Error TypeError: The 'Readonly' property cannot be reassigned. */ diff --git a/ets2panda/test/ast/compiler/ets/readonlyType_3.ets b/ets2panda/test/ast/compiler/ets/readonlyType_3.ets index 9c5e0a1b2d1a3bd2e3d5f1ac5ab95cde0cdd0bf9..7ef6007189723be2af27f5629dc3f96beeb8a05a 100644 --- a/ets2panda/test/ast/compiler/ets/readonlyType_3.ets +++ b/ets2panda/test/ast/compiler/ets/readonlyType_3.ets @@ -14,11 +14,11 @@ */ class A { - /* @@ label */fld: Number = 2 + fld: Number = 2 foo (a0: Readonly) { - /* @@ label2 */a0.fld = 5 + /* @@ label */a0.fld = 5 } } /* @@@ label Error TypeError: Cannot assign to a readonly variable fld */ -/* @@@ label2 Error TypeError: The 'Readonly' property cannot be reassigned. */ +/* @@@ label Error TypeError: The 'Readonly' property cannot be reassigned. */ diff --git a/ets2panda/test/ast/compiler/ets/readonlyType_4.ets b/ets2panda/test/ast/compiler/ets/readonlyType_4.ets index 82d154132b47ae32d209acffbfd80d41d9cbb49f..4d3bf9936efd7457877d54af9f0ce3af04dac227 100644 --- a/ets2panda/test/ast/compiler/ets/readonlyType_4.ets +++ b/ets2panda/test/ast/compiler/ets/readonlyType_4.ets @@ -14,7 +14,7 @@ */ class A { - /* @@ label */fld: Number = 2 + fld: Number = 2 foo (a0: A) { a0.fld = 5 } @@ -25,9 +25,9 @@ class B extends A {} class C extends B { fld: Number = 2 foo (a0: Readonly) { - /* @@ label2 */a0.fld = 6 + /* @@ label */a0.fld = 6 } } /* @@@ label Error TypeError: Cannot assign to a readonly variable fld */ -/* @@@ label2 Error TypeError: The 'Readonly' property cannot be reassigned. */ +/* @@@ label Error TypeError: The 'Readonly' property cannot be reassigned. */ diff --git a/ets2panda/test/ast/compiler/ets/update_const.ets b/ets2panda/test/ast/compiler/ets/update_const.ets new file mode 100644 index 0000000000000000000000000000000000000000..15ffd430673492a7e6c6f970300e7698288ade34 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/update_const.ets @@ -0,0 +1,21 @@ +/* + * 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. + */ + +function main(): void { + const c = 0; + /* @@ label */++c; +} + +/* @@@ label Error TypeError: Cannot assign to a constant variable c */ diff --git a/ets2panda/test/ast/parser/ets/global_const_vars4.ets b/ets2panda/test/ast/parser/ets/global_const_vars4.ets index f1d3d77aa9e8e3aadeaf80ed5fd67b0e33f99b77..7cc9886ebc6b5cb5cf5321c5c4dd6320e20ce637 100644 --- a/ets2panda/test/ast/parser/ets/global_const_vars4.ets +++ b/ets2panda/test/ast/parser/ets/global_const_vars4.ets @@ -14,14 +14,14 @@ */ class ABC { - public readonly /* @@ label */date: Date = new Date(); + public readonly date: Date = new Date(); constructor() { this.init() } private init(): void { - this.date = new Date(); + /* @@ label */this.date = new Date(); } public main(): void {} diff --git a/ets2panda/test/ast/parser/ets/readonly_reference_CTE_err_elimilate.ets b/ets2panda/test/ast/parser/ets/readonly_reference_CTE_err_elimilate.ets index 7452c971205878f7d592b5a637eda01237f0ed65..435c77d20aaa0727515fb8496a8b59e5cdaa12c6 100644 --- a/ets2panda/test/ast/parser/ets/readonly_reference_CTE_err_elimilate.ets +++ b/ets2panda/test/ast/parser/ets/readonly_reference_CTE_err_elimilate.ets @@ -14,7 +14,7 @@ */ class A { - /* @@ label */x: int = 444 + x: int = 444 } let changeAbleVar = new A(); @@ -22,7 +22,7 @@ let changeAbleVar = new A(); changeAbleVar.x=333 function bar(a:Readonly){ - /* @@ label2 */a.x=111 + /* @@ label */a.x=111 console.log(a) } @@ -30,4 +30,4 @@ bar(changeAbleVar) /* @@@ label Error TypeError: Cannot assign to a readonly variable x */ -/* @@@ label2 Error TypeError: The 'Readonly' property cannot be reassigned. */ +/* @@@ label Error TypeError: The 'Readonly' property cannot be reassigned. */ diff --git a/ets2panda/test/ast/parser/ets/unexpected_token_61.ets b/ets2panda/test/ast/parser/ets/unexpected_token_61.ets index bf42eb21ad12c372ce6964fd6eb85e6e2fb388df..76c41c3aa432f2414e2e4c27053c8a3c7f2d55c7 100644 --- a/ets2panda/test/ast/parser/ets/unexpected_token_61.ets +++ b/ets2panda/test/ast/parser/ets/unexpected_token_61.ets @@ -14,9 +14,10 @@ */ function main(): void { - for (const i : int = 0 /* @@ label1 */i < 10; i++) { + for (const i : int = 0 /* @@ label1 */i < 10; /* @@ label */i++) { console.log("a") } } +/* @@@ label Error TypeError: Cannot assign to a constant variable i */ /* @@@ label1 Error SyntaxError: Expected ';', got 'identification literal'. */ diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index aad4b5292a7218957136b01566f5610209990c7d..b313a435558a450efc2801bac4a989867757245f 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -185,7 +185,6 @@ semantic: - name: ASSIGNMENT_INVALID_LHS id: 25 message: "Invalid left-hand side of assignment expression" - code_fix_ids: [FixConvertConstToLet] - name: ASSIGN_TO_READONLY_PROP id: 209 @@ -522,6 +521,7 @@ semantic: - name: FIELD_ASSIGN_TYPE_MISMATCH id: 298 message: "Cannot assign to a {} variable {}" + code_fix_ids: [FixConvertConstToLet] - name: FIELD_REASSIGNMENT id: 297