diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 67ab7afd77226602a07514a1fa00f9febb6a8a93..1d4eaa2bb0022549fd773e3463708ea4d5a34693 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -752,9 +752,11 @@ public: void GenerateGetterSetterPropertyAndMethod(ir::ClassProperty *originalProp, ETSObjectType *classType); void SetupGetterSetterFlags(ir::ClassProperty *originalProp, ETSObjectType *classType, ir::MethodDefinition *getter, ir::MethodDefinition *setter, const bool inExternal); - Type *GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident); + Type *GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident, + std::unordered_set *moduleStackCache = nullptr); void ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration *importDecl, - checker::ETSObjectType *lastObjectType, ir::Identifier *ident); + checker::ETSObjectType *lastObjectType, ir::Identifier *ident, + std::unordered_set *moduleStackCache = nullptr); bool CheckValidEqualReferenceType(checker::Type *const leftType, checker::Type *const rightType); bool CheckVoidAnnotation(const ir::ETSPrimitiveType *typeAnnotation); void ETSObjectTypeDeclNode(ETSChecker *checker, ETSObjectType *const objectType); diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index 9cbdb705276c3778031bdbfe2876f3ad7fe192d7..287db28d98bde6ae2663334678cc28af2d9d5453 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -3200,13 +3200,14 @@ bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, cons } void ETSChecker::ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration *importDecl, - checker::ETSObjectType *lastObjectType, ir::Identifier *ident) + checker::ETSObjectType *lastObjectType, ir::Identifier *ident, + std::unordered_set *moduleStackCache) { for (auto item : VarBinder()->AsETSBinder()->ReExportImports()) { if (importDecl->ResolvedSource() != item->GetProgramPath().Mutf8()) { continue; } - auto *reExportType = GetImportSpecifierObjectType(item->GetETSImportDeclarations(), ident); + auto *reExportType = GetImportSpecifierObjectType(item->GetETSImportDeclarations(), ident, moduleStackCache); if (reExportType->IsTypeError()) { continue; } @@ -3221,7 +3222,8 @@ void ETSChecker::ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclarati } } -Type *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident) +Type *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident, + std::unordered_set *moduleStackCache) { auto importPath = importDecl->IsPureDynamic() ? importDecl->DeclPath() : importDecl->ResolvedSource(); parser::Program *program = @@ -3229,6 +3231,16 @@ Type *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importD if (program == nullptr) { return GlobalTypeError(); } + std::unordered_set localCache; + if (moduleStackCache == nullptr) { + moduleStackCache = &localCache; + } + RecursionPreserver guard(*moduleStackCache, program); + + if (*guard) { + LogError(diagnostic::CYCLIC_EXPORT, ident->Start()); + return GlobalTypeError(); + } auto const moduleName = program->ModuleName(); auto const internalName = @@ -3247,7 +3259,7 @@ Type *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importD ES2PANDA_ASSERT(rootVar != nullptr); rootVar->SetTsType(moduleObjectType); - ImportNamespaceObjectTypeAddReExportType(importDecl, moduleObjectType, ident); + ImportNamespaceObjectTypeAddReExportType(importDecl, moduleObjectType, ident, moduleStackCache); SetPropertiesForModuleObject(moduleObjectType, importPath, importDecl->Specifiers()[0]->IsImportNamespaceSpecifier() ? nullptr : importDecl); SetrModuleObjectTsType(ident, moduleObjectType); diff --git a/ets2panda/test/ast/parser/ets/re_export/cyclic_export_file1.ets b/ets2panda/test/ast/parser/ets/re_export/cyclic_export_file1.ets new file mode 100644 index 0000000000000000000000000000000000000000..87aee176d052811f418601ad35db7685f92d348b --- /dev/null +++ b/ets2panda/test/ast/parser/ets/re_export/cyclic_export_file1.ets @@ -0,0 +1,17 @@ +/* + * 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 * from './cyclic_export_file2.ets'; +export let fromA = 111; \ No newline at end of file diff --git a/ets2panda/test/ast/parser/ets/re_export/cyclic_export_file2.ets b/ets2panda/test/ast/parser/ets/re_export/cyclic_export_file2.ets new file mode 100644 index 0000000000000000000000000000000000000000..2bdfd9939b82f72af21889f1c9e27fad6f86a642 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/re_export/cyclic_export_file2.ets @@ -0,0 +1,17 @@ +/* + * 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 * from './cyclic_export_file1.ets'; +export let fromB = 111; \ No newline at end of file diff --git a/ets2panda/test/ast/parser/ets/re_export/cyclic_export_test.ets b/ets2panda/test/ast/parser/ets/re_export/cyclic_export_test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f70ac972f820ee57145251f35a23f5f40de36a04 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/re_export/cyclic_export_test.ets @@ -0,0 +1,20 @@ +/* + * 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 * as a from './cyclic_export_file1.ets' +import * as b from './cyclic_export_file2.ets' + +/* @@? 16:13 Error TypeError: Cyclic export detected re-exports */ +/* @@? 17:13 Error TypeError: Cyclic export detected re-exports */ \ No newline at end of file diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index 5d663722425154b4fd1bd960eecd55d153f815aa..7b870463ef4a7e5504547a6eb16a5f127c4d2f52 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -356,6 +356,10 @@ semantic: id: 380 message: "Class's super type is itself" +- name: CYCLIC_EXPORT + id: 67633 + message: "Cyclic export detected re-exports" + - name: CYCLIC_INHERITANCE id: 310 message: "Cyclic inheritance involving {}."