From c0bccba092b01554110052a567bde731aa7c68a9 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Thu, 5 Mar 2026 18:47:16 +0000 Subject: [PATCH] implements PR013 --- .../compiler/pbs/linking/PbsLinkErrors.java | 2 + .../linking/PbsModuleVisibilityValidator.java | 29 +++++++++-- .../pbs/linking/PbsModuleVisibilityTest.java | 48 +++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/linking/PbsLinkErrors.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/linking/PbsLinkErrors.java index 8ae3f692..87521d5a 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/linking/PbsLinkErrors.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/linking/PbsLinkErrors.java @@ -8,5 +8,7 @@ public enum PbsLinkErrors { E_LINK_DUPLICATE_BARREL_ENTRY, E_LINK_UNRESOLVED_BARREL_ENTRY, E_LINK_AMBIGUOUS_BARREL_ENTRY, + E_LINK_IMPORT_MODULE_NOT_FOUND, + E_LINK_IMPORT_SYMBOL_UNRESOLVED, E_LINK_IMPORT_SYMBOL_NOT_PUBLIC } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/linking/PbsModuleVisibilityValidator.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/linking/PbsModuleVisibilityValidator.java index d648a1da..05ec71aa 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/linking/PbsModuleVisibilityValidator.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/linking/PbsModuleVisibilityValidator.java @@ -74,6 +74,7 @@ public final class PbsModuleVisibilityValidator { final var barrel = module.barrelFiles().getFirst(); final var declarations = collectDeclarations(module, nameTable); + exports.declaredNameIds.addAll(declarations.declaredNameIds); final Map seenNonFunctionEntries = new HashMap<>(); final Map seenFunctionEntries = new HashMap<>(); @@ -179,20 +180,36 @@ public final class PbsModuleVisibilityValidator { final DiagnosticSink diagnostics) { for (final var source : module.sourceFiles()) { for (final var importDecl : source.ast().imports()) { - if (importDecl.items().isEmpty()) { - continue; - } - final var targetModule = new ModuleRefKey( importDecl.moduleRef().project(), importDecl.moduleRef().pathSegments()); final var targetExports = exportsByModule.get(targetModule); if (targetExports == null) { + p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + PbsLinkErrors.E_LINK_IMPORT_MODULE_NOT_FOUND.name(), + "Import target module %s was not found".formatted(displayModule(targetModule)), + importDecl.moduleRef().span()); + continue; + } + + if (importDecl.items().isEmpty()) { continue; } for (final var importItem : importDecl.items()) { final var importedNameId = nameTable.register(importItem.name()); + if (targetExports.publicNameIds.contains(importedNameId)) { + continue; + } + if (!targetExports.declaredNameIds.contains(importedNameId)) { + p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, + PbsLinkErrors.E_LINK_IMPORT_SYMBOL_UNRESOLVED.name(), + "Symbol '%s' does not exist in module %s".formatted( + importItem.name(), + displayModule(targetModule)), + importItem.span()); + continue; + } if (!targetExports.publicNameIds.contains(importedNameId)) { p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, PbsLinkErrors.E_LINK_IMPORT_SYMBOL_NOT_PUBLIC.name(), @@ -224,6 +241,7 @@ public final class PbsModuleVisibilityValidator { declarations.functionsBySignature .computeIfAbsent(key, ignored -> new ArrayList<>()) .add(functionDecl.span()); + declarations.declaredNameIds.add(key.nameId()); continue; } @@ -259,6 +277,7 @@ public final class PbsModuleVisibilityValidator { declarations.nonFunctionsByKindAndName .computeIfAbsent(key, ignored -> new ArrayList<>()) .add(span); + declarations.declaredNameIds.add(key.nameId()); } private FunctionSymbolKey functionKey( @@ -467,9 +486,11 @@ public final class PbsModuleVisibilityValidator { private static final class ModuleDeclarations { private final Map> nonFunctionsByKindAndName = new HashMap<>(); private final Map> functionsBySignature = new HashMap<>(); + private final Set declaredNameIds = new HashSet<>(); } private static final class ModuleExports { + private final Set declaredNameIds = new HashSet<>(); private final Set publicNameIds = new HashSet<>(); } } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/linking/PbsModuleVisibilityTest.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/linking/PbsModuleVisibilityTest.java index 512a9e76..68b269dc 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/linking/PbsModuleVisibilityTest.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/test/java/p/studio/compiler/pbs/linking/PbsModuleVisibilityTest.java @@ -149,6 +149,54 @@ class PbsModuleVisibilityTest { assertTrue(importVisibilityDiagnostics.getFirst().getMessage().contains("secret")); } + @Test + void shouldRejectImportFromMissingModule() { + final var diagnostics = DiagnosticSink.empty(); + final var nextFileId = new AtomicInteger(0); + + final var moduleApp = module("core", "app", List.of( + """ + import { open } from @core:missing; + fn use() -> int { return 1; } + """ + ), """ + pub fn use() -> int; + """, nextFileId, diagnostics); + + new PbsModuleVisibilityValidator().validate(ReadOnlyList.wrap(List.of(moduleApp)), diagnostics); + + assertTrue(diagnostics.stream().anyMatch(d -> + d.getCode().equals(PbsLinkErrors.E_LINK_IMPORT_MODULE_NOT_FOUND.name()))); + } + + @Test + void shouldRejectImportOfUnknownSymbol() { + final var diagnostics = DiagnosticSink.empty(); + final var nextFileId = new AtomicInteger(0); + + final var moduleMath = module("core", "math", List.of( + """ + fn open() -> int { return 2; } + """ + ), """ + pub fn open() -> int; + """, nextFileId, diagnostics); + + final var moduleApp = module("core", "app", List.of( + """ + import { missing } from @core:math; + fn use() -> int { return 1; } + """ + ), """ + pub fn use() -> int; + """, nextFileId, diagnostics); + + new PbsModuleVisibilityValidator().validate(ReadOnlyList.wrap(List.of(moduleMath, moduleApp)), diagnostics); + + assertTrue(diagnostics.stream().anyMatch(d -> + d.getCode().equals(PbsLinkErrors.E_LINK_IMPORT_SYMBOL_UNRESOLVED.name()))); + } + @Test void shouldResolveBarrelFunctionWithResultReturnSurface() { final var diagnostics = DiagnosticSink.empty();