summaryrefslogtreecommitdiff
path: root/src/test/java/org/zwobble
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2026-06-26 11:32:16 +0100
committerMichael Williamson <mike@zwobble.org>2026-06-26 11:32:29 +0100
commit09bbd799f6c12f75af13f24e82f907d6770132e8 (patch)
treed192057079082ff1a39ffe6aa6b2e7ba5478c655 /src/test/java/org/zwobble
parenta447077c21f103bc3ce9edd67fc7d503750645c3 (diff)
Check field presence for sum variants
Diffstat (limited to 'src/test/java/org/zwobble')
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java11
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java67
2 files changed, 70 insertions, 8 deletions
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
index f45b215..81bf9b8 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
@@ -5,17 +5,12 @@ import org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceNode;
import org.zwobble.hobgoblin.compiler.ast.typed.TypedStructDefinitionNode;
import org.zwobble.hobgoblin.compiler.ast.typed.TypedStructFieldDefinitionNode;
import org.zwobble.hobgoblin.compiler.ast.typed.TypedTypeLevelExpressionNode;
-import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedArb;
-import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceNode;
-import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode;
-import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode;
-import org.zwobble.hobgoblin.compiler.types.Field;
-import org.zwobble.hobgoblin.compiler.types.NamespaceName;
-import org.zwobble.hobgoblin.compiler.types.SimpleNativeType;
-import org.zwobble.hobgoblin.compiler.types.StructType;
+import org.zwobble.hobgoblin.compiler.ast.untyped.*;
+import org.zwobble.hobgoblin.compiler.types.*;
import java.util.List;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.zwobble.hobgoblin.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement;
import static org.zwobble.precisely.AssertThat.assertThat;
import static org.zwobble.precisely.Matchers.*;
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java
index 57c40f5..36a4647 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java
@@ -10,6 +10,7 @@ import org.zwobble.hobgoblin.compiler.types.*;
import java.util.List;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.zwobble.hobgoblin.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement;
import static org.zwobble.precisely.AssertThat.assertThat;
import static org.zwobble.precisely.Matchers.*;
@@ -130,4 +131,70 @@ public class TypeCheckerSumDefinitionTests {
)
));
}
+
+ @Test
+ public void givenVariantTypeIsDefinedFirstWhenVariantTypeIsMissingFieldThenErrorIsThrown() {
+ var int32Type = SimpleNativeType.builtin("Int32");
+ var untyped = UntypedSumDefinitionNode.arbitrary()
+ .withName("Shape")
+ .withVariants(List.of(
+ UntypedSumVariantDefinitionNode.arbitrary()
+ .withType(UntypedTypeLevelReferenceNode.arbitrary().withName("Square"))
+ .build()
+ ))
+ .withFields(List.of(
+ UntypedStructFieldDefinitionNode.arbitrary()
+ .withName("area")
+ .withType(UntypedTypeLevelReferenceNode.arbitrary().withName("Int32"))
+ .build()
+ ))
+ .build();
+ var globalContext = TypeCheckerGlobalContext.initial();
+ globalContext.addNativeType(int32Type);
+ var namespaceName = NamespaceName.of("a", "b");
+ var namespaceContext = globalContext
+ .enterNamespace(namespaceName);
+ var variantType = new StructType(namespaceName, "Square");
+ namespaceContext.declare("Square", new TypeLevelValueType(variantType));
+ namespaceContext.defineStructType(variantType, List.of());
+
+ var error = assertThrows(
+ SubtypeIsMissingFieldError.class,
+ () -> typeCheckNamespaceStatement(untyped, namespaceContext)
+ );
+
+ assertThat(error.supertype(), equalTo(new SumType(namespaceName, "Shape")));
+ assertThat(error.subtype(), equalTo(variantType));
+ assertThat(error.fieldName(), equalTo("area"));
+ }
+
+ @Test
+ public void givenSumTypeIsDefinedFirstWhenVariantTypeIsMissingFieldThenErrorIsThrown() {
+ var int32Type = SimpleNativeType.builtin("Int32");
+ var untyped = UntypedStructDefinitionNode.arbitrary()
+ .withName("Square")
+ .build();
+ var globalContext = TypeCheckerGlobalContext.initial();
+ globalContext.addNativeType(int32Type);
+ var namespaceName = NamespaceName.of("a", "b");
+ var namespaceContext = globalContext
+ .enterNamespace(namespaceName);
+ var sumType = new SumType(namespaceName, "Shape");
+ var variantType = new StructType(namespaceName, "Square");
+ namespaceContext.declare("Sum", new TypeLevelValueType(sumType));
+ namespaceContext.defineSumType(
+ sumType,
+ List.of(variantType),
+ List.of(new Field("area", int32Type))
+ );
+
+ var error = assertThrows(
+ SubtypeIsMissingFieldError.class,
+ () -> typeCheckNamespaceStatement(untyped, namespaceContext)
+ );
+
+ assertThat(error.supertype(), equalTo(sumType));
+ assertThat(error.subtype(), equalTo(variantType));
+ assertThat(error.fieldName(), equalTo("area"));
+ }
}