Improve error message when amending a property with no value set (#1867)

Improve error message when amending a property with no value set

This decorates error messages with the amendment node.
This commit is contained in:
Daniel Chao authored and GitHub committed 2026-09-21 06:54:22 -07:00
1 parent e0b3b87e19
commit cbffd6c114
9 files changed
+149 -1

No files matched your search

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,13 @@
*/
package org.pkl.core.ast.expression.literal;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.pkl.core.ast.ExpressionNode;
import org.pkl.core.runtime.*;
@@ -32,6 +34,8 @@ public abstract class EmptyObjectLiteralNode extends ExpressionNode {
super(sourceSection);
}
protected abstract Object executeWithParent(VirtualFrame frame, Object parent);
protected abstract ExpressionNode getParentNode();
@Specialization
@@ -69,4 +73,26 @@ public abstract class EmptyObjectLiteralNode extends ExpressionNode {
VmUtils.checkIsInstantiable(VmUtils.getClass(parent), getParentNode());
throw exceptionBuilder().unreachableCode().build();
}
@Override
public final Object executeGeneric(VirtualFrame frame) {
Object parent;
try {
parent = getParentNode().executeGeneric(frame);
} catch (VmException e) {
CompilerDirectives.transferToInterpreter();
// include object amendment in the error message if error found during eval of parent
if (e.getSourceSection() != null
&& !e.getSourceSection().equals(sourceSection)
// don't include if the originating error's source section is within the bounds of our own
// source section.
&& !(VmUtils.sourceSectionContains(sourceSection, e.getSourceSection()))) {
e.getInsertedStackFrames()
.putIfAbsent(
getRootNode().getCallTarget(), VmUtils.createStackFrame(sourceSection, null));
}
throw e;
}
return executeWithParent(frame, parent);
}
}
@@ -28,6 +28,7 @@ import org.pkl.core.ast.type.TypeNode;
import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.BaseModule;
import org.pkl.core.runtime.VmClass;
import org.pkl.core.runtime.VmException;
import org.pkl.core.runtime.VmFunction;
import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmNull;
@@ -58,6 +59,8 @@ public abstract class ObjectLiteralNode extends ExpressionNode {
this.parameterTypes = parameterTypes;
}
protected abstract Object executeWithParent(VirtualFrame frame, Object parent);
protected abstract ExpressionNode getParentNode();
protected abstract ObjectLiteralNode copy(ExpressionNode newParentNode);
@@ -114,4 +117,27 @@ public abstract class ObjectLiteralNode extends ExpressionNode {
LoopNode.reportLoopCount(this, count);
return value;
}
@Override
public final Object executeGeneric(VirtualFrame frame) {
Object parent;
try {
parent = getParentNode().executeGeneric(frame);
} catch (VmException e) {
CompilerDirectives.transferToInterpreter();
// include object amendment in the error message if error found during eval of parent
if (e.getSourceSection() != null
&& !e.getSourceSection().equals(sourceSection)
// don't include if the originating error's source section is within the bounds of our own
// source section.
&& !(VmUtils.sourceSectionContains(sourceSection, e.getSourceSection()))) {
e.getInsertedStackFrames()
.putIfAbsent(
getRootNode().getCallTarget(),
VmUtils.createStackFrame(sourceSection, qualifiedScopeName));
}
throw e;
}
return executeWithParent(frame, parent);
}
}
@@ -1166,4 +1166,12 @@ public final class VmUtils {
return builder.toString();
}
public static boolean sourceSectionContains(SourceSection thisSource, SourceSection otherSource) {
if (!thisSource.getSource().equals(otherSource.getSource())) {
return false;
}
return thisSource.getCharIndex() <= otherSource.getCharIndex()
&& thisSource.getCharEndIndex() >= otherSource.getCharEndIndex();
}
}
@@ -0,0 +1,7 @@
class Example {
aa: Listing<String> | String
}
res: Example = new {
aa { "hello" }
}
@@ -0,0 +1,9 @@
open class Parent {
bb: *String | Int
}
class Example extends Parent {
bb {}
}
res: Example = new {}
@@ -0,0 +1,8 @@
class Example {
aa: Listing<String> | String
bb: Listing<String> = aa
}
res: Example = new {
bb {}
}
@@ -0,0 +1,20 @@
–– Pkl Error ––
Tried to read property `aa` but its value is undefined.
x | aa: Listing<String> | String
^^
at amendPropertyWithNoDefault1#Example.aa (file:///$snippetsDir/input/errors/amendPropertyWithNoDefault1.pkl)
The above error occurred when rendering path `res.aa` of module `file:///$snippetsDir/input/errors/amendPropertyWithNoDefault1.pkl`.
x | aa { "hello" }
^^^^^^^^^^^^^^
at amendPropertyWithNoDefault1#res.aa (file:///$snippetsDir/input/errors/amendPropertyWithNoDefault1.pkl)
xxx | renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
^^^^
at pkl.base#Module.output.bytes (pkl:base)
@@ -0,0 +1,20 @@
–– Pkl Error ––
Tried to read property `bb` but its value is undefined.
x | bb: *String | Int
^^
at amendPropertyWithNoDefault2#Parent.bb (file:///$snippetsDir/input/errors/amendPropertyWithNoDefault2.pkl)
The above error occurred when rendering path `res.bb` of module `file:///$snippetsDir/input/errors/amendPropertyWithNoDefault2.pkl`.
x | bb {}
^^^^^
at <unknown> (file:///$snippetsDir/input/errors/amendPropertyWithNoDefault2.pkl)
xxx | renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
^^^^
at pkl.base#Module.output.bytes (pkl:base)
@@ -0,0 +1,24 @@
–– Pkl Error ––
Tried to read property `aa` but its value is undefined.
x | aa: Listing<String> | String
^^
at amendPropertyWithNoDefault3#Example.aa (file:///$snippetsDir/input/errors/amendPropertyWithNoDefault3.pkl)
The above error occurred when rendering path `res.aa` of module `file:///$snippetsDir/input/errors/amendPropertyWithNoDefault3.pkl`.
x | bb: Listing<String> = aa
^^
at amendPropertyWithNoDefault3#Example.bb (file:///$snippetsDir/input/errors/amendPropertyWithNoDefault3.pkl)
x | bb {}
^^^^^
at <unknown> (file:///$snippetsDir/input/errors/amendPropertyWithNoDefault3.pkl)
xxx | renderer.renderDocument(value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
at pkl.base#Module.output.text (pkl:base)
xxx | if (renderer is BytesRenderer) renderer.renderDocument(value) else text.encodeToBytes("UTF-8")
^^^^
at pkl.base#Module.output.bytes (pkl:base)