Skip to content

Commit 43e9f2e

Browse files
committed
Add error hint when accessing the default value of a union type with no default
1 parent b92c773 commit 43e9f2e

File tree

9 files changed

+93
-4
lines changed

9 files changed

+93
-4
lines changed

pkl-core/src/main/java/org/pkl/core/ast/member/DefaultPropertyBodyNode.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,9 @@
1919
import com.oracle.truffle.api.frame.VirtualFrame;
2020
import com.oracle.truffle.api.source.SourceSection;
2121
import org.pkl.core.ast.ExpressionNode;
22+
import org.pkl.core.ast.type.TypeNode.ConstrainedTypeNode;
23+
import org.pkl.core.ast.type.TypeNode.TypeAliasTypeNode;
24+
import org.pkl.core.ast.type.TypeNode.UnionTypeNode;
2225
import org.pkl.core.runtime.Identifier;
2326
import org.pkl.core.runtime.VmUtils;
2427
import org.pkl.core.util.Nullable;
@@ -50,8 +53,30 @@ public Object executeGeneric(VirtualFrame frame) {
5053
}
5154

5255
CompilerDirectives.transferToInterpreter();
56+
57+
// attempt to give a hint when a union type is missing a default
58+
String aliasName = null;
59+
String unionTypeSource = null;
60+
if (typeNode != null) {
61+
var tn = typeNode.getTypeNode();
62+
while (true) {
63+
if (tn instanceof TypeAliasTypeNode typeAlias) {
64+
aliasName = typeAlias.getVmTypeAlias().getSimpleName();
65+
tn = typeAlias.getVmTypeAlias().getTypeNode();
66+
} else if (tn instanceof ConstrainedTypeNode constrained) {
67+
tn = constrained.getBaseTypeNode();
68+
} else {
69+
break;
70+
}
71+
}
72+
if (tn instanceof UnionTypeNode union) {
73+
unionTypeSource = union.getSourceSection().getCharacters().toString();
74+
}
75+
}
76+
5377
throw exceptionBuilder()
54-
.undefinedPropertyValue(propertyName, VmUtils.getReceiver(frame))
78+
.undefinedPropertyValue(
79+
propertyName, VmUtils.getReceiver(frame), unionTypeSource, aliasName)
5580
.build();
5681
}
5782
}

pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2598,6 +2598,10 @@ public Object executeAndSet(VirtualFrame frame, Object value) {
25982598
return childNode.createDefaultValue(language, headerSection, qualifiedName);
25992599
}
26002600

2601+
public TypeNode getBaseTypeNode() {
2602+
return childNode;
2603+
}
2604+
26012605
public SourceSection getBaseTypeSection() {
26022606
return childNode.getSourceSection();
26032607
}

pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionBuilder.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
2+
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -124,9 +124,19 @@ public VmExceptionBuilder undefinedValue() {
124124
return withExternalMessage("undefinedValue");
125125
}
126126

127-
public VmExceptionBuilder undefinedPropertyValue(Identifier propertyName, Object receiver) {
127+
public VmExceptionBuilder undefinedPropertyValue(
128+
Identifier propertyName,
129+
Object receiver,
130+
@Nullable String unionTypeSource,
131+
@Nullable String typeAliasName) {
128132
kind = VmException.Kind.UNDEFINED_VALUE;
129133
this.receiver = receiver;
134+
if (unionTypeSource != null && typeAliasName != null) {
135+
return withExternalMessage(
136+
"undefinedPropertyValueUnionAlias", propertyName, unionTypeSource, typeAliasName);
137+
} else if (unionTypeSource != null) {
138+
return withExternalMessage("undefinedPropertyValueUnion", propertyName, unionTypeSource);
139+
}
130140
return withExternalMessage("undefinedPropertyValue", propertyName);
131141
}
132142

pkl-core/src/main/resources/org/pkl/core/errorMessages.properties

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,14 @@ Undefined value.
436436
undefinedPropertyValue=\
437437
Tried to read property `{0}` but its value is undefined.
438438

439+
undefinedPropertyValueUnion=\
440+
Tried to read property `{0}` but its value is undefined.\n\
441+
Union type `{1}` has no default member.
442+
443+
undefinedPropertyValueUnionAlias=\
444+
Tried to read property `{0}` but its value is undefined.\n\
445+
Union type `{1}` (from type alias `{2}`) has no default member.
446+
439447
cannotFindModule=\
440448
Cannot find module `{0}`.
441449

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
local typealias B = Listing<String> | String
2+
3+
b: B
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
local typealias B = Listing<String> | String
2+
local typealias C = B(true)
3+
4+
c: C

pkl-core/src/test/files/LanguageSnippetTests/output/errors/noDefault2.err

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
–– Pkl Error ––
22
Tried to read property `foo` but its value is undefined.
3+
Union type `Foo|"baz"` has no default member.
34

45
x | foo: Foo|"baz"
56
^^^
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
–– Pkl Error ––
2+
Tried to read property `b` but its value is undefined.
3+
Union type `Listing<String> | String` (from type alias `B`) has no default member.
4+
5+
x | b: B
6+
^
7+
at noDefault3#b (file:///$snippetsDir/input/errors/noDefault3.pkl)
8+
9+
The above error occurred when rendering path `b` of module `file:///$snippetsDir/input/errors/noDefault3.pkl`.
10+
11+
xxx | renderer.renderDocument(value)
12+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
at pkl.base#Module.output.text (pkl:base)
14+
15+
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
16+
^^^^
17+
at pkl.base#Module.output.bytes (pkl:base)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
–– Pkl Error ––
2+
Tried to read property `c` but its value is undefined.
3+
Union type `Listing<String> | String` (from type alias `B`) has no default member.
4+
5+
x | c: C
6+
^
7+
at noDefault4#c (file:///$snippetsDir/input/errors/noDefault4.pkl)
8+
9+
The above error occurred when rendering path `c` of module `file:///$snippetsDir/input/errors/noDefault4.pkl`.
10+
11+
xxx | renderer.renderDocument(value)
12+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
at pkl.base#Module.output.text (pkl:base)
14+
15+
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
16+
^^^^
17+
at pkl.base#Module.output.bytes (pkl:base)

0 commit comments

Comments
 (0)