diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-06-19 17:47:22 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-06-19 17:47:22 +0100 |
| commit | 81d6985af5c1d43a8d03af18eca3ec51242362c3 (patch) | |
| tree | 310c990ca568acd369960c1a42f58e5da9d76724 /src | |
| parent | f818776dba8bda2bf6e423da991190f3f4b4ff3f (diff) | |
Throw error on unknown namespace field
Diffstat (limited to 'src')
4 files changed, 59 insertions, 6 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java index e9e7e74..78bdc97 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java @@ -31,7 +31,13 @@ public class TypeChecker { var importedNamespaceFields = context.namespaceFields(importedNamespace); for (var importedName : untypedImport.importedNames()) { - namespaceContext.declare(importedName, importedNamespaceFields.get(importedName)); + var fieldType = importedNamespaceFields.fieldType(importedName) + .orElseThrow(() -> new UnknownNamespaceFieldError( + importedName, + importedNamespace, + untypedImport.source() + )); + namespaceContext.declare(importedName, fieldType); } } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UnknownNamespaceFieldError.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UnknownNamespaceFieldError.java new file mode 100644 index 0000000..52f8b0a --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/UnknownNamespaceFieldError.java @@ -0,0 +1,24 @@ +package org.zwobble.hobgoblin.compiler.typechecker; + +import org.zwobble.hobgoblin.compiler.errors.SourceError; +import org.zwobble.hobgoblin.compiler.sources.Source; +import org.zwobble.hobgoblin.compiler.types.NamespaceName; + +public class UnknownNamespaceFieldError extends SourceError { + private final String fieldName; + private final NamespaceName namespaceName; + + public UnknownNamespaceFieldError(String fieldName, NamespaceName namespaceName, Source source) { + super("Unknown field " + fieldName + " on " + namespaceName, source); + this.fieldName = fieldName; + this.namespaceName = namespaceName; + } + + public String fieldName() { + return fieldName; + } + + public NamespaceName namespaceName() { + return namespaceName; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/Fields.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/Fields.java index 4fc364e..013a844 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/types/Fields.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/Fields.java @@ -1,6 +1,7 @@ package org.zwobble.hobgoblin.compiler.types; import java.util.List; +import java.util.Optional; public class Fields { private final List<Field> fields; @@ -9,12 +10,10 @@ public class Fields { this.fields = fields; } - public Type get(String name) { + public Optional<Type> fieldType(String name) { return fields.stream() .filter(field -> field.name().equals(name)) .findFirst() - // TODO: better error handling - .orElseThrow() - .type(); + .map(field -> field.type()); } } diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceTests.java index c4b31b8..3559578 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceTests.java @@ -9,6 +9,7 @@ import org.zwobble.hobgoblin.compiler.types.*; import java.util.List; import java.util.Optional; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.zwobble.hobgoblin.compiler.types.TypeMatchers.isMetaType; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; @@ -42,7 +43,7 @@ public class TypeCheckerNamespaceTests { )) )); assertThat( - context.namespaceFields(NamespaceName.of("a", "b")).get("X"), + context.namespaceFields(NamespaceName.of("a", "b")).fieldType("X").orElseThrow(), isMetaType(new StructType(NamespaceName.of("a", "b"), "X")) ); } @@ -97,4 +98,27 @@ public class TypeCheckerNamespaceTests { ) ); } + + @Test + public void whenNamespaceHasNoSuchFieldThenErrorIsThrown() { + var untyped = UntypedNamespaceNode.arbitrary() + .withImports(List.of( + UntypedImportNode.arbitrary() + .withAncestorDepth(Optional.empty()) + .withNamespaceName(NamespaceName.of("a")) + .withImportedNames(List.of("A")) + .build() + )) + .build(); + var context = TypeCheckerContextArb.globalContext(); + context.defineNamespace(NamespaceName.of("a"), List.of()); + + var error = assertThrows( + UnknownNamespaceFieldError.class, + () -> TypeChecker.typeCheckNamespace(untyped, context) + ); + + assertThat(error.fieldName(), equalTo("A")); + assertThat(error.namespaceName(), equalTo(NamespaceName.of("a"))); + } } |
