diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-06-26 11:32:16 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-06-26 11:32:29 +0100 |
| commit | 09bbd799f6c12f75af13f24e82f907d6770132e8 (patch) | |
| tree | d192057079082ff1a39ffe6aa6b2e7ba5478c655 /src/test/java/org/zwobble | |
| parent | a447077c21f103bc3ce9edd67fc7d503750645c3 (diff) | |
Check field presence for sum variants
Diffstat (limited to 'src/test/java/org/zwobble')
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")); + } } |
