implements PR013
This commit is contained in:
parent
7dfaf6b49b
commit
c0bccba092
@ -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
|
||||
}
|
||||
|
||||
@ -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<NonFunctionSymbolKey, Span> seenNonFunctionEntries = new HashMap<>();
|
||||
final Map<FunctionSymbolKey, Span> 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<NonFunctionSymbolKey, List<Span>> nonFunctionsByKindAndName = new HashMap<>();
|
||||
private final Map<FunctionSymbolKey, List<Span>> functionsBySignature = new HashMap<>();
|
||||
private final Set<NameId> declaredNameIds = new HashSet<>();
|
||||
}
|
||||
|
||||
private static final class ModuleExports {
|
||||
private final Set<NameId> declaredNameIds = new HashSet<>();
|
||||
private final Set<NameId> publicNameIds = new HashSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user