From 01fa1216781cb3d20744080fe40bddd8db490ca3 Mon Sep 17 00:00:00 2001 From: nikozer Date: Tue, 8 Jul 2025 13:41:52 +0300 Subject: [PATCH] allow alias exports with same name Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICNSYV Test: --astchecker Signed-off-by: nikozer --- .../invariants/importExportAccessValid.cpp | 2 +- .../variableNameIdentifierNameSame.cpp | 4 +- ets2panda/checker/ets/etsWarningAnalyzer.cpp | 2 +- ets2panda/checker/ets/helpers.cpp | 4 +- .../topLevelStmts/globalDeclTransformer.cpp | 5 +-- .../ets/topLevelStmts/importExportDecls.cpp | 37 ++++++++----------- .../ets/topLevelStmts/importExportDecls.h | 3 +- ets2panda/ir/ets/etsModule.cpp | 2 +- .../ir/statements/functionDeclaration.cpp | 7 +++- .../ets/export_and_export_type_class.ets | 2 - .../ets/export_and_export_type_interface.ets | 2 - ...ame_type_at_decl_and_selective_binding.ets | 3 -- .../ets/export_type_class_multiple_times.ets | 3 +- .../export_type_interface_multiple_times.ets | 3 -- .../ets/import_tests/export_multi_error.ets | 7 +--- .../export_type_several_times_1.ets | 24 ++++++++++++ .../export_type_several_times_2.ets | 27 ++++++++++++++ .../import_same_type_form_alias_1.ets | 23 ++++++++++++ .../import_same_type_form_alias_2.ets | 23 ++++++++++++ .../import_same_type_form_alias_3.ets | 24 ++++++++++++ .../selective_export_clashing_exports_1.ets | 4 +- .../selective_export_clashing_exports_2.ets | 4 +- .../ast/compiler/ets/namespaceExport_neg.ets | 2 - .../parser/ets/import_tests/type/type_2.ets | 2 - .../parser/ets/import_tests/type/type_3.ets | 3 +- .../astchecker/astchecker-ets-ignored.txt | 6 +++ ets2panda/util/diagnostic/syntax.yaml | 6 ++- ets2panda/util/diagnostic/warning.yaml | 4 ++ ets2panda/varbinder/ETSBinder.cpp | 25 +++++++++---- ets2panda/varbinder/ETSBinder.h | 6 ++- ets2panda/varbinder/scope.cpp | 2 +- 31 files changed, 196 insertions(+), 75 deletions(-) create mode 100644 ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/export_type_several_times_1.ets create mode 100644 ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/export_type_several_times_2.ets create mode 100644 ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_1.ets create mode 100644 ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_2.ets create mode 100644 ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_3.ets diff --git a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp index bf7351956a..594d130b5b 100644 --- a/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp +++ b/ets2panda/ast_verifier/invariants/importExportAccessValid.cpp @@ -70,7 +70,7 @@ bool ImportExportAccessValid::ValidateExport(const varbinder::Variable *var) if (node == nullptr) { return false; } - return node->IsExported(); + return node->IsExported() || node->HasExportAlias(); } bool ImportExportAccessValid::InvariantImportExportMethod(const std::unordered_set &importedVariables, diff --git a/ets2panda/ast_verifier/invariants/variableNameIdentifierNameSame.cpp b/ets2panda/ast_verifier/invariants/variableNameIdentifierNameSame.cpp index f3d85ee0a0..3b82e3e138 100644 --- a/ets2panda/ast_verifier/invariants/variableNameIdentifierNameSame.cpp +++ b/ets2panda/ast_verifier/invariants/variableNameIdentifierNameSame.cpp @@ -32,8 +32,8 @@ namespace ark::es2panda::compiler::ast_verifier { } const auto variableNode = variable->Declaration()->Node(); // NOTE(psaykerone): skip because, this exceptions need to be fixed in checker and lowering - if (variableNode->IsExported() || variableNode->IsDefaultExported() || id->Name().Utf8().find("field") == 0 || - variable->Name().Utf8().find("field") == 0) { + if (variableNode->IsExported() || variableNode->IsDefaultExported() || variableNode->HasExportAlias() || + id->Name().Utf8().find("field") == 0 || variable->Name().Utf8().find("field") == 0) { return {CheckDecision::CORRECT, CheckAction::CONTINUE}; } if (id->Name() == variable->Name()) { diff --git a/ets2panda/checker/ets/etsWarningAnalyzer.cpp b/ets2panda/checker/ets/etsWarningAnalyzer.cpp index 8320d38cbc..4376b07ba8 100644 --- a/ets2panda/checker/ets/etsWarningAnalyzer.cpp +++ b/ets2panda/checker/ets/etsWarningAnalyzer.cpp @@ -44,7 +44,7 @@ void ETSWarningAnalyzer::AnalyzeClassDefForFinalModifier(const ir::ClassDefiniti ES2PANDA_ASSERT(classDef != nullptr); if (program_ == nullptr || classDef->IsFinal() || classDef->IsAbstract() || classDef->IsStatic() || - classDef->IsGlobal() || classDef->IsExported()) { + classDef->IsGlobal() || classDef->IsExported() || classDef->HasExportAlias()) { return; } diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index d53d72e322..d0743fac22 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -1695,7 +1695,9 @@ void ETSChecker::BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleO for (auto [_, var] : bindings) { (void)_; auto [found, aliasedName] = FindSpecifierForModuleObject(importDecl, var->AsLocalVariable()->Name()); - if ((var->AsLocalVariable()->Declaration()->Node()->IsExported()) && found) { + if ((var->AsLocalVariable()->Declaration()->Node()->IsExported() || + var->AsLocalVariable()->Declaration()->Node()->HasExportAlias()) && + found) { if (!aliasedName.Empty()) { moduleObjType->AddReExportAlias(var->Declaration()->Name(), aliasedName); } diff --git a/ets2panda/compiler/lowering/ets/topLevelStmts/globalDeclTransformer.cpp b/ets2panda/compiler/lowering/ets/topLevelStmts/globalDeclTransformer.cpp index ee9a0572a2..422dc6de01 100644 --- a/ets2panda/compiler/lowering/ets/topLevelStmts/globalDeclTransformer.cpp +++ b/ets2panda/compiler/lowering/ets/topLevelStmts/globalDeclTransformer.cpp @@ -62,7 +62,7 @@ void GlobalDeclTransformer::VisitFunctionDeclaration(ir::FunctionDeclaration *fu method->SetRange(funcDecl->Range()); method->Function()->SetAnnotations(std::move(funcDecl->Annotations())); - if (funcDecl->Function()->IsExported() && funcDecl->Function()->HasExportAlias()) { + if (funcDecl->Function()->HasExportAlias()) { method->AddAstNodeFlags(ir::AstNodeFlags::HAS_EXPORT_ALIAS); } @@ -96,8 +96,7 @@ void GlobalDeclTransformer::VisitVariableDeclaration(ir::VariableDeclaration *va field->SetAnnotations(std::move(propAnnotations)); } - if ((varDecl->IsExported() || declarator->IsExported()) && - (varDecl->HasExportAlias() || declarator->HasExportAlias())) { + if (varDecl->HasExportAlias() || declarator->HasExportAlias()) { field->AddAstNodeFlags(ir::AstNodeFlags::HAS_EXPORT_ALIAS); } diff --git a/ets2panda/compiler/lowering/ets/topLevelStmts/importExportDecls.cpp b/ets2panda/compiler/lowering/ets/topLevelStmts/importExportDecls.cpp index adbb17ad0f..9de0c504a0 100644 --- a/ets2panda/compiler/lowering/ets/topLevelStmts/importExportDecls.cpp +++ b/ets2panda/compiler/lowering/ets/topLevelStmts/importExportDecls.cpp @@ -14,6 +14,7 @@ */ #include "compiler/lowering/ets/topLevelStmts/importExportDecls.h" +#include "generated/diagnostic.h" namespace ark::es2panda::compiler { @@ -69,37 +70,33 @@ GlobalClassHandler::ModuleDependencies ImportExportDecls::HandleGlobalStmts(Aren void ImportExportDecls::PopulateAliasMap(const ir::ExportNamedDeclaration *decl, const util::StringView &path) { for (auto spec : decl->Specifiers()) { - if (!varbinder_->AddSelectiveExportAlias(path, spec->Local()->Name(), spec->Exported()->Name(), decl)) { - parser_->LogError(diagnostic::DUPLICATE_EXPORT_NAME, {spec->Local()->Name().Mutf8()}, - spec->Exported()->Start()); + if (!varbinder_->AddSelectiveExportAlias(parser_, path, spec->Local()->Name(), spec->Exported()->Name(), + decl)) { + parser_->LogError(diagnostic::CANNOT_EXPORT_DIFFERENT_OBJECTS_WITH_SAME_NAME, + {spec->Local()->Name().Mutf8()}, spec->Exported()->Start()); lastExportErrorPos_ = lexer::SourcePosition(); } } } -void ImportExportDecls::AddExportFlags(ir::AstNode *node, util::StringView originalFieldName, - lexer::SourcePosition startLoc, bool exportedWithAlias) +void ImportExportDecls::AddExportFlags(ir::AstNode *node, util::StringView originalFieldName, bool exportedWithAlias) { - if ((node->Modifiers() & ir::ModifierFlags::EXPORTED) != 0) { - // Note (oeotvos) Needs to be discussed, whether we would like to allow exporting the same program - // element using its original name and also an alias, like: export {test_func, test_func as foo}. - parser_->LogError(diagnostic::ALREADY_EXPORTED, {originalFieldName.Mutf8()}, startLoc); + if (exportedWithAlias) { + node->AddAstNodeFlags(ir::AstNodeFlags::HAS_EXPORT_ALIAS); + return; } if (originalFieldName == exportDefaultName_) { node->AddModifier(ir::ModifierFlags::DEFAULT_EXPORT); } else { node->AddModifier(ir::ModifierFlags::EXPORT); } - if (exportedWithAlias) { - node->AddAstNodeFlags(ir::AstNodeFlags::HAS_EXPORT_ALIAS); - } } void ImportExportDecls::PopulateAliasMap(const ir::TSTypeAliasDeclaration *decl, const util::StringView &path) { - if (!varbinder_->AddSelectiveExportAlias(path, decl->Id()->AsIdentifier()->Name(), + if (!varbinder_->AddSelectiveExportAlias(parser_, path, decl->Id()->AsIdentifier()->Name(), decl->Id()->AsIdentifier()->Name(), decl)) { - parser_->LogError(diagnostic::DUPLICATE_EXPORT_NAME, {decl->Id()->AsIdentifier()->Name().Mutf8()}, - lastExportErrorPos_); + parser_->LogError(diagnostic::CANNOT_EXPORT_DIFFERENT_OBJECTS_WITH_SAME_NAME, + {decl->Id()->AsIdentifier()->Name().Mutf8()}, lastExportErrorPos_); lastExportErrorPos_ = lexer::SourcePosition(); } } @@ -119,9 +116,9 @@ void ImportExportDecls::HandleSelectiveExportWithAlias(util::StringView original } if (variableDeclarator != nullptr) { - AddExportFlags(variableDeclarator, originalFieldName, startLoc, exportedWithAlias); + AddExportFlags(variableDeclarator, originalFieldName, exportedWithAlias); } else { - AddExportFlags(field, originalFieldName, startLoc, exportedWithAlias); + AddExportFlags(field, originalFieldName, exportedWithAlias); } } @@ -201,11 +198,7 @@ void ImportExportDecls::VisitExportNamedDeclaration(ir::ExportNamedDeclaration * } exportDefaultName_ = local->Name(); } - - if (!exportNameMap_.emplace(local->Name(), local->Start()).second) { - lastExportErrorPos_ = local->Start(); - parser_->LogError(diagnostic::DUPLICATE_EXPORT_NAME, {local->Name().Mutf8()}, lastExportErrorPos_); - } + exportNameMap_.emplace(local->Name(), local->Start()); } } diff --git a/ets2panda/compiler/lowering/ets/topLevelStmts/importExportDecls.h b/ets2panda/compiler/lowering/ets/topLevelStmts/importExportDecls.h index c1b50a29aa..81351b38e3 100644 --- a/ets2panda/compiler/lowering/ets/topLevelStmts/importExportDecls.h +++ b/ets2panda/compiler/lowering/ets/topLevelStmts/importExportDecls.h @@ -64,8 +64,7 @@ public: void HandleSimpleType(std::set &exportedStatements, ir::Statement *stmt, util::StringView name); void VerifySingleExportDefault(const ArenaVector &programs); - void AddExportFlags(ir::AstNode *node, util::StringView originalFieldName, lexer::SourcePosition startLoc, - bool exportedWithAlias); + void AddExportFlags(ir::AstNode *node, util::StringView originalFieldName, bool exportedWithAlias); void HandleSelectiveExportWithAlias(util::StringView originalFieldName, util::StringView exportName, lexer::SourcePosition startLoc); void PopulateAliasMap(const ir::ExportNamedDeclaration *decl, const util::StringView &path); diff --git a/ets2panda/ir/ets/etsModule.cpp b/ets2panda/ir/ets/etsModule.cpp index 3fea029e81..e6cfc877a6 100644 --- a/ets2panda/ir/ets/etsModule.cpp +++ b/ets2panda/ir/ets/etsModule.cpp @@ -29,7 +29,7 @@ void ETSModule::Dump(ir::SrcDumper *dumper) const dumper->Add("export default "); } - if (IsDeclare()) { + if (IsDeclare() && !(parent_ != nullptr && parent_->IsDeclare())) { dumper->Add("declare "); } diff --git a/ets2panda/ir/statements/functionDeclaration.cpp b/ets2panda/ir/statements/functionDeclaration.cpp index 21fb0d5f85..ff6028046f 100644 --- a/ets2panda/ir/statements/functionDeclaration.cpp +++ b/ets2panda/ir/statements/functionDeclaration.cpp @@ -74,7 +74,12 @@ void FunctionDeclaration::Dump(ir::SrcDumper *dumper) const if (func_->IsNative()) { dumper->Add("native "); } - if (func_->IsDeclare()) { + if (IsExported()) { + dumper->Add("export "); + } else if (IsDefaultExported()) { + dumper->Add("export default "); + } + if (func_->IsDeclare() && !(parent_ != nullptr && parent_->IsDeclare())) { dumper->Add("declare "); } if (func_->IsAsyncFunc()) { diff --git a/ets2panda/test/ast/compiler/ets/export_and_export_type_class.ets b/ets2panda/test/ast/compiler/ets/export_and_export_type_class.ets index 57d8b40dda..ba28d7a7b8 100644 --- a/ets2panda/test/ast/compiler/ets/export_and_export_type_class.ets +++ b/ets2panda/test/ast/compiler/ets/export_and_export_type_class.ets @@ -15,5 +15,3 @@ export class A {} export type {/* @@ label */A} - -/* @@@ label Error SyntaxError: Cannot export 'A', it was already exported. */ diff --git a/ets2panda/test/ast/compiler/ets/export_and_export_type_interface.ets b/ets2panda/test/ast/compiler/ets/export_and_export_type_interface.ets index 0b4bef4431..201b28a8dd 100644 --- a/ets2panda/test/ast/compiler/ets/export_and_export_type_interface.ets +++ b/ets2panda/test/ast/compiler/ets/export_and_export_type_interface.ets @@ -15,5 +15,3 @@ export interface I {} export type {/* @@ label */I} - -/* @@@ label Error SyntaxError: Cannot export 'I', it was already exported. */ diff --git a/ets2panda/test/ast/compiler/ets/export_same_type_at_decl_and_selective_binding.ets b/ets2panda/test/ast/compiler/ets/export_same_type_at_decl_and_selective_binding.ets index e566df5aae..206cbcabbf 100644 --- a/ets2panda/test/ast/compiler/ets/export_same_type_at_decl_and_selective_binding.ets +++ b/ets2panda/test/ast/compiler/ets/export_same_type_at_decl_and_selective_binding.ets @@ -15,6 +15,3 @@ export type class A {} export type {/* @@ label */A} - - -/* @@@ label Error SyntaxError: Cannot export 'A', it was already exported. */ diff --git a/ets2panda/test/ast/compiler/ets/export_type_class_multiple_times.ets b/ets2panda/test/ast/compiler/ets/export_type_class_multiple_times.ets index 4609849c5d..4a2147118c 100644 --- a/ets2panda/test/ast/compiler/ets/export_type_class_multiple_times.ets +++ b/ets2panda/test/ast/compiler/ets/export_type_class_multiple_times.ets @@ -19,5 +19,4 @@ export type {A} export type MyA = A export type {MyA} -/* @@? 20:14 Error SyntaxError: The given name 'MyA' is already used in another export. */ -/* @@? 20:14 Error SyntaxError: Cannot export 'MyA', it was already exported. */ +/* @@? 16:1 Warning Warning: Duplicated export aliases for 'MyA'. */ diff --git a/ets2panda/test/ast/compiler/ets/export_type_interface_multiple_times.ets b/ets2panda/test/ast/compiler/ets/export_type_interface_multiple_times.ets index 0a34b8a2cc..c714b60e24 100644 --- a/ets2panda/test/ast/compiler/ets/export_type_interface_multiple_times.ets +++ b/ets2panda/test/ast/compiler/ets/export_type_interface_multiple_times.ets @@ -18,6 +18,3 @@ interface I {} export type {I} export type MyI = I export type {MyI} - -/* @@? 20:14 Error SyntaxError: The given name 'MyI' is already used in another export. */ -/* @@? 20:14 Error SyntaxError: Cannot export 'MyI', it was already exported. */ diff --git a/ets2panda/test/ast/compiler/ets/import_tests/export_multi_error.ets b/ets2panda/test/ast/compiler/ets/import_tests/export_multi_error.ets index c02f3a526a..1bcecf5a36 100644 --- a/ets2panda/test/ast/compiler/ets/import_tests/export_multi_error.ets +++ b/ets2panda/test/ast/compiler/ets/import_tests/export_multi_error.ets @@ -58,11 +58,6 @@ export default function TestFuncToo(): void {} /*-----------------*/ /* @@? 16:10 Error SyntaxError: Cannot find name 'foo' to export. */ -/* @@? 27:14 Error SyntaxError: The given name 'foo2' is already used in another export. */ -/* @@? 27:27 Error SyntaxError: The given name 'foo2' is already used in another export. */ -/* @@? 33:11 Error SyntaxError: Cannot export 'foo3', it was already exported. */ -/* @@? 39:19 Error SyntaxError: Cannot export 'A', it was already exported. */ -/* @@? 46:14 Error SyntaxError: The given name 'MyI' is already used in another export. */ -/* @@? 46:14 Error SyntaxError: Cannot export 'MyI', it was already exported. */ +/* @@? 27:14 Error SyntaxError: Cannot export two different names with the same export alias name 'foo2'. */ /* @@? 56:16 Error TypeError: Only one default export is allowed in a module */ /* @@? 57:16 Error TypeError: Only one default export is allowed in a module */ diff --git a/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/export_type_several_times_1.ets b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/export_type_several_times_1.ets new file mode 100644 index 0000000000..3262d1a839 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/export_type_several_times_1.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. + */ + +export function foo(): void {} +export class A {} + +export { + foo as f1, + foo as f2, + A as a1, + A as a2 +} diff --git a/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/export_type_several_times_2.ets b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/export_type_several_times_2.ets new file mode 100644 index 0000000000..54f88f12e3 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/export_type_several_times_2.ets @@ -0,0 +1,27 @@ +/* + * 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 foo(): void {} +class A {} +let a = new A() + +export { + foo as f1, + foo as f2, + A as a1, + A as a2, + a as a3, + a as a4 +} diff --git a/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_1.ets b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_1.ets new file mode 100644 index 0000000000..f3d2d5d4dc --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_1.ets @@ -0,0 +1,23 @@ +/* + * 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. + */ + +import { foo, f1, f2, A, a1, a2 } from "./export_type_several_times_1.ets" + +foo() +f1() +f2() +let c: A = new A() +let c1: A = new a1() +let c2: A = new a2() \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_2.ets b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_2.ets new file mode 100644 index 0000000000..91be1a73db --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_2.ets @@ -0,0 +1,23 @@ +/* + * 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. + */ + +import { f1, f2, a1, a2, a3, a4 } from "./export_type_several_times_2.ets" + +f1() +f2() +let c1: a1 = new a1() +let c2: a2 = new a2() +c1 = a3 +c2 = a4 \ No newline at end of file diff --git a/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_3.ets b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_3.ets new file mode 100644 index 0000000000..fec071802f --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/import_same_type_form_alias_3.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. + */ + +import { foo, f1, f2, A, a1, a2 } from "./export_type_several_times_2.ets" + +f1() +f2() +let c1: a1 = new a1() +let c2: a2 = new a2() + +/* @@? 16:10 Error TypeError: Cannot find imported element 'foo' */ +/* @@? 16:23 Error TypeError: Cannot find imported element 'A' */ diff --git a/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/selective_export_clashing_exports_1.ets b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/selective_export_clashing_exports_1.ets index 453e606be3..bddda705c6 100644 --- a/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/selective_export_clashing_exports_1.ets +++ b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/selective_export_clashing_exports_1.ets @@ -16,7 +16,5 @@ export function foo(): void {} export { - foo as /* @@ label */bar + foo as bar } - -/* @@@ label Error SyntaxError: Cannot export 'foo', it was already exported. */ diff --git a/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/selective_export_clashing_exports_2.ets b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/selective_export_clashing_exports_2.ets index b5eeab80f6..a75a5dbac4 100644 --- a/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/selective_export_clashing_exports_2.ets +++ b/ets2panda/test/ast/compiler/ets/import_tests/selective_export_tests/selective_export_clashing_exports_2.ets @@ -16,7 +16,5 @@ export default function foo(): void {} export { - foo as /* @@ label */bar + foo as bar } - -/* @@@ label Error SyntaxError: Cannot export 'foo', it was already exported. */ diff --git a/ets2panda/test/ast/compiler/ets/namespaceExport_neg.ets b/ets2panda/test/ast/compiler/ets/namespaceExport_neg.ets index e742a4cdc8..fb3ea3bbd8 100644 --- a/ets2panda/test/ast/compiler/ets/namespaceExport_neg.ets +++ b/ets2panda/test/ast/compiler/ets/namespaceExport_neg.ets @@ -30,5 +30,3 @@ declare namespace A { } export default A -/* @@? 27:20 Error SyntaxError: Cannot export 'A', it was already exported. */ - diff --git a/ets2panda/test/ast/parser/ets/import_tests/type/type_2.ets b/ets2panda/test/ast/parser/ets/import_tests/type/type_2.ets index aa88265896..7cab79843a 100644 --- a/ets2panda/test/ast/parser/ets/import_tests/type/type_2.ets +++ b/ets2panda/test/ast/parser/ets/import_tests/type/type_2.ets @@ -16,5 +16,3 @@ export class A {} export type {A as AA}; - -/* @@? 18:19 Error SyntaxError: Cannot export 'A', it was already exported. */ \ No newline at end of file diff --git a/ets2panda/test/ast/parser/ets/import_tests/type/type_3.ets b/ets2panda/test/ast/parser/ets/import_tests/type/type_3.ets index 0b9164b765..ea9cf69480 100644 --- a/ets2panda/test/ast/parser/ets/import_tests/type/type_3.ets +++ b/ets2panda/test/ast/parser/ets/import_tests/type/type_3.ets @@ -19,5 +19,4 @@ class TestClass {} export {foo} export type {TestClass as foo} -/* @@? 20:14 Error SyntaxError: The given name 'foo' is already used in another export. */ -/* @@? 20:27 Error SyntaxError: The given name 'foo' is already used in another export. */ +/* @@? 20:14 Error SyntaxError: Cannot export two different names with the same export alias name 'foo'. */ diff --git a/ets2panda/test/test-lists/astchecker/astchecker-ets-ignored.txt b/ets2panda/test/test-lists/astchecker/astchecker-ets-ignored.txt index 3888be099f..27aedf969a 100644 --- a/ets2panda/test/test-lists/astchecker/astchecker-ets-ignored.txt +++ b/ets2panda/test/test-lists/astchecker/astchecker-ets-ignored.txt @@ -102,3 +102,9 @@ ast/compiler/ets/lambda_infer_type/lambda_param_type_cannot_be_determined.ets # Issue: #24605 incorrect column ast/parser/ets/named_types_2.ets + + +# Issue: #27365 Cannot add correct comment to astchecker to see warning with 0:0 position (duplicate export warning) +ast/compiler/ets/export_type_class_multiple_times.ets +ast/compiler/ets/export_type_interface_multiple_times.ets +ast/compiler/ets/import_tests/export_multi_error.ets \ No newline at end of file diff --git a/ets2panda/util/diagnostic/syntax.yaml b/ets2panda/util/diagnostic/syntax.yaml index 72003d7cc4..be7c754eb5 100644 --- a/ets2panda/util/diagnostic/syntax.yaml +++ b/ets2panda/util/diagnostic/syntax.yaml @@ -80,6 +80,10 @@ syntax: id: 17 message: "Index type expected in index signature." +- name: CANNOT_EXPORT_DIFFERENT_OBJECTS_WITH_SAME_NAME + id: 344 + message: "Cannot export two different names with the same export alias name '{}'." + - name: INDEX_TYPE_NOT_NUMBER id: 18 message: "Index type must be number in index signature." @@ -1240,6 +1244,6 @@ syntax: id: 306 message: "Function expressions are not supported, use arrow functions instead" -- name: DEFAULT_PARAM_IN_DECLARE +- name: DEFAULT_PARAM_IN_DECLARE id: 307 message: "A parameter initializer is only allowed in a function or constructor implementation." diff --git a/ets2panda/util/diagnostic/warning.yaml b/ets2panda/util/diagnostic/warning.yaml index 264a442c2d..c778be08f0 100644 --- a/ets2panda/util/diagnostic/warning.yaml +++ b/ets2panda/util/diagnostic/warning.yaml @@ -28,6 +28,10 @@ warning: id: 4 message: "Boost Equality Statement. Change sides of binary expression." +- name: DUPLICATE_EXPORT_ALIASES + id: 73 + message: "Duplicated export aliases for '{}'." + - name: REPLACE_ASYNC_FUNCTION_WITH_COROUTINE id: 5 message: "Replace asynchronous function with coroutine." diff --git a/ets2panda/varbinder/ETSBinder.cpp b/ets2panda/varbinder/ETSBinder.cpp index 43eb2ceffc..2389c72b23 100644 --- a/ets2panda/varbinder/ETSBinder.cpp +++ b/ets2panda/varbinder/ETSBinder.cpp @@ -211,11 +211,20 @@ void ETSBinder::ResolveReferencesForScopeWithContext(ir::AstNode *node, Scope *s ResolveReference(node); } -bool ETSBinder::AddSelectiveExportAlias(util::StringView const &path, util::StringView const &key, - util::StringView const &value, ir::AstNode const *decl) noexcept +// export { a as b } value => a, key => b +// value == value and key == key => Warning, value == value and key != key => Ok, value != value and key == key => CTE +bool ETSBinder::AddSelectiveExportAlias(parser::ETSParser *parser, util::StringView const &path, + util::StringView const &key, util::StringView const &value, + ir::AstNode const *decl) noexcept { if (auto foundMap = selectiveExportAliasMultimap_.find(path); foundMap != selectiveExportAliasMultimap_.end()) { - return foundMap->second.insert({key, std::make_pair(value, decl)}).second; + auto inserted = foundMap->second.insert({key, std::make_pair(value, decl)}).second; + if (UNLIKELY(!inserted && foundMap->second.find(key)->second.first == value)) { + parser->DiagnosticEngine().Log( + {util::DiagnosticType::WARNING, diagnostic::DUPLICATE_EXPORT_ALIASES, {key}, decl->Start()}); + return true; + } + return inserted; } ArenaMap> map(Allocator()->Adapter()); @@ -805,7 +814,8 @@ Variable *ETSBinder::FindImportSpecifiersVariable(const util::StringView &import static bool IsExportedVariable(varbinder::Variable *const var) { return var != nullptr && - (var->Declaration()->Node()->IsExported() || var->Declaration()->Node()->IsDefaultExported()); + (var->Declaration()->Node()->IsExported() || var->Declaration()->Node()->IsDefaultExported() || + var->Declaration()->Node()->HasExportAlias()); } std::pair ETSBinder::FindImportDeclInExports( @@ -942,7 +952,7 @@ void ETSBinder::ValidateImportVariable(const ir::AstNode *node, const util::Stri { if (node->IsDefaultExported()) { ThrowError(importPath->Start(), diagnostic::DEFAULT_EXPORT_DIRECT_IMPORTED); - } else if (!node->IsExported() && !node->IsDefaultExported()) { + } else if (!node->IsExported() && !node->IsDefaultExported() && !node->HasExportAlias()) { ThrowError(importPath->Start(), diagnostic::IMPORTED_NOT_EXPORTED, {imported}); } } @@ -1064,8 +1074,9 @@ bool ETSBinder::AddImportSpecifiersToTopBindings(Span re } // The first part of the condition will be true, if something was given an alias when exported, but we try - // to import it using its original name. - if (nameToSearchFor == imported && var->Declaration()->Node()->HasExportAlias()) { + // to import it using its original name and if original name is not exported. + if (nameToSearchFor == imported && var->Declaration()->Node()->HasExportAlias() && + !var->Declaration()->Node()->IsExported()) { ThrowError(importSpecifier->Start(), diagnostic::IMPORT_NOT_FOUND, {imported}); return false; } diff --git a/ets2panda/varbinder/ETSBinder.h b/ets2panda/varbinder/ETSBinder.h index bc0096047f..4b446aa2e0 100644 --- a/ets2panda/varbinder/ETSBinder.h +++ b/ets2panda/varbinder/ETSBinder.h @@ -23,6 +23,7 @@ #include "ir/expressions/identifier.h" #include "ir/module/importSpecifier.h" #include "ir/statements/annotationDeclaration.h" +#include "parser/ETSparser.h" namespace ark::es2panda::ir { class ETSImportDeclaration; @@ -256,8 +257,9 @@ public: void ResolveReferencesForScopeWithContext(ir::AstNode *node, Scope *scope); - [[nodiscard]] bool AddSelectiveExportAlias(util::StringView const &path, util::StringView const &key, - util::StringView const &value, ir::AstNode const *decl) noexcept; + [[nodiscard]] bool AddSelectiveExportAlias(parser::ETSParser *parser, util::StringView const &path, + util::StringView const &key, util::StringView const &value, + ir::AstNode const *decl) noexcept; [[nodiscard]] const ModulesToExportedNamesWithAliases &GetSelectiveExportAliasMultimap() const noexcept { diff --git a/ets2panda/varbinder/scope.cpp b/ets2panda/varbinder/scope.cpp index 49b0538b7b..4f40ed1606 100644 --- a/ets2panda/varbinder/scope.cpp +++ b/ets2panda/varbinder/scope.cpp @@ -671,7 +671,7 @@ Scope::InsertResult GlobalScope::InsertImpl(const util::StringView &name, Variab ES2PANDA_ASSERT(var->Declaration()->Name().Utf8().find(compiler::Signatures::ETS_GLOBAL) == std::string::npos); const auto *const node = var->Declaration()->Node(); - if (!(node->IsExported() || node->IsDefaultExported())) { + if (!(node->IsExported() || node->IsDefaultExported() || node->HasExportAlias())) { return Scope::InsertResult {Bindings().end(), false}; } } -- Gitee