Skip to content

Commit 32478ad

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

File tree

11 files changed

+153
-5
lines changed

11 files changed

+153
-5
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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,16 @@ 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+
\n\
442+
Union type `{1}` has no default member.
443+
444+
undefinedPropertyValueUnionAlias=\
445+
Tried to read property `{0}` but its value is undefined.\n\
446+
\n\
447+
Union type `{1}` (from typealias `{2}`) has no default member.
448+
439449
cannotFindModule=\
440450
Cannot find module `{0}`.
441451

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
local typealias B = Listing<String> | String
2+
local typealias C = B(true)
3+
4+
local class A {
5+
a: Listing<String> | String
6+
b: B
7+
c: C
8+
}
9+
10+
result =
11+
new A {
12+
c {}
13+
}.c
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
local typealias B = Listing<String> | String
2+
local typealias C = B
3+
4+
local class A {
5+
a: Listing<String> | String
6+
b: B
7+
c: C
8+
}
9+
10+
result =
11+
new A {
12+
b {}
13+
}.b
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
local typealias B = Listing<String> | String
2+
local typealias C = B
3+
4+
local class A {
5+
a: Listing<String> | String
6+
b: B
7+
c: C
8+
}
9+
10+
result =
11+
new A {
12+
a {}
13+
}.a
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
–– Pkl Error ––
2+
Tried to read property `c` but its value is undefined.
3+
4+
x | c: C
5+
^
6+
at amendUnionAliasAliasWithoutDefault#A.c (file:///$snippetsDir/input/errors/amendUnionAliasAliasWithoutDefault.pkl)
7+
8+
Union type `Listing<String> | String` (from typealias `B`) has no default member.
9+
10+
xx | new A {
11+
^^^^^^^
12+
at amendUnionAliasAliasWithoutDefault#result (file:///$snippetsDir/input/errors/amendUnionAliasAliasWithoutDefault.pkl)
13+
14+
xxx | renderer.renderDocument(value)
15+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
at pkl.base#Module.output.text (pkl:base)
17+
18+
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
19+
^^^^
20+
at pkl.base#Module.output.bytes (pkl:base)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
–– Pkl Error ––
2+
Tried to read property `b` but its value is undefined.
3+
4+
x | b: B
5+
^
6+
at amendUnionAliasWithoutDefault#A.b (file:///$snippetsDir/input/errors/amendUnionAliasWithoutDefault.pkl)
7+
8+
Union type `Listing<String> | String` (from typealias `B`) has no default member.
9+
10+
xx | new A {
11+
^^^^^^^
12+
at amendUnionAliasWithoutDefault#result (file:///$snippetsDir/input/errors/amendUnionAliasWithoutDefault.pkl)
13+
14+
xxx | renderer.renderDocument(value)
15+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
at pkl.base#Module.output.text (pkl:base)
17+
18+
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
19+
^^^^
20+
at pkl.base#Module.output.bytes (pkl:base)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
–– Pkl Error ––
2+
Tried to read property `a` but its value is undefined.
3+
4+
x | a: Listing<String> | String
5+
^
6+
at amendUnionWithoutDefault#A.a (file:///$snippetsDir/input/errors/amendUnionWithoutDefault.pkl)
7+
8+
Union type `Listing<String> | String` has no default member.
9+
10+
xx | new A {
11+
^^^^^^^
12+
at amendUnionWithoutDefault#result (file:///$snippetsDir/input/errors/amendUnionWithoutDefault.pkl)
13+
14+
xxx | renderer.renderDocument(value)
15+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
at pkl.base#Module.output.text (pkl:base)
17+
18+
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
19+
^^^^
20+
at pkl.base#Module.output.bytes (pkl:base)

0 commit comments

Comments
 (0)