aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr/eval.cc
diff options
context:
space:
mode:
authorQyriad <qyriad@qyriad.me>2024-06-22 21:59:47 -0600
committerQyriad <qyriad@qyriad.me>2024-07-04 17:42:35 -0600
commit14bf54bd393f1b48ba519d104abd53434fc18e75 (patch)
treebd10ce351fc870f9e113c3ef3f6e95f4b4144887 /src/libexpr/eval.cc
parentd00edfb28d0a52d9acd392c582a43f98e773cf4c (diff)
trace which part of `foo.bar.baz` errors
Turns errors like: let somepkg.src = throw "invalid foobar"; in somepkg.src.meta error: … while evaluating the attribute 'src.meta' at «string»:2:3: 1| let 2| somepkg.src = throw "invalid foobar"; | ^ 3| in somepkg.src.meta … while calling the 'throw' builtin at «string»:2:17: 1| let 2| somepkg.src = throw "invalid foobar"; | ^ 3| in somepkg.src.meta error: invalid foobar into errors like: let somepkg.src = throw "invalid foobar"; in somepkg.src.meta error: … while evaluating the attribute 'src.meta' at «string»:2:3: 1| let 2| somepkg.src = throw "invalid foobar"; | ^ 3| in somepkg.src.meta … while evaluating 'somepkg.src' to select 'meta' on it at «string»:3:4: 2| somepkg.src = throw "invalid foobar"; 3| in somepkg.src.meta | ^ … while calling the 'throw' builtin at «string»:2:17: 1| let 2| somepkg.src = throw "invalid foobar"; | ^ 3| in somepkg.src.meta error: invalid foobar And for type errors, from: let somepkg.src = "I'm not an attrset"; in somepkg.src.meta error: … while evaluating the attribute 'src.meta' at «string»:2:3: 1| let 2| somepkg.src = "I'm not an attrset"; | ^ 3| in somepkg.src.meta … while selecting an attribute at «string»:3:4: 2| somepkg.src = "I'm not an attrset"; 3| in somepkg.src.meta | ^ error: expected a set but found a string: "I'm not an attrset" into: let somepkg.src = "I'm not an attrset"; in somepkg.src.meta error: … while evaluating the attribute 'src.meta' at «string»:2:3: 1| let 2| somepkg.src = "I'm not an attrset"; | ^ 3| in somepkg.src.meta … while selecting 'meta' on 'somepkg.src' at «string»:3:4: 2| somepkg.src = "I'm not an attrset"; 3| in somepkg.src.meta | ^ error: expected a set but found a string: "I'm not an attrset" For the low price of an enumerate() and a lambda you too can have the incorrect line of code actually show up in the trace! Change-Id: Ic1491c86e33c167891bdac9adad6224784760bd6
Diffstat (limited to 'src/libexpr/eval.cc')
-rw-r--r--src/libexpr/eval.cc43
1 files changed, 40 insertions, 3 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index b01867a09..56581cc19 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1458,12 +1458,46 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
showAttrPath(state, env, attrPath))
: nullptr;
- for (auto const & currentAttrName : attrPath) {
+ for (auto const & [partIdx, currentAttrName] : enumerate(attrPath)) {
state.nrLookups++;
Symbol const name = getName(currentAttrName, state, env);
- state.forceValue(*vCurrent, pos);
+ // For formatting errors, which should be done only when needed.
+ auto partsSoFar = [&]() -> std::string {
+ std::stringstream ss;
+ // We start with the base thing this ExprSelect is selecting on.
+ assert(this->e != nullptr);
+ this->e->show(state.symbols, ss);
+
+ // Then grab each part of the attr path up to this one.
+ assert(partIdx < attrPath.size());
+ std::span<AttrName> const parts(
+ attrPath.begin(),
+ attrPath.begin() + partIdx
+ );
+
+ // And convert them to strings and join them.
+ for (auto const & part : parts) {
+ auto const partName = getName(part, state, env);
+ ss << "." << state.symbols[partName];
+ }
+
+ return ss.str();
+ };
+
+ try {
+ state.forceValue(*vCurrent, pos);
+ } catch (Error & e) {
+ state.addErrorTrace(
+ e,
+ getPos(),
+ "while evaluating '%s' to select '%s' on it",
+ partsSoFar(),
+ state.symbols[name]
+ );
+ throw;
+ }
if (vCurrent->type() != nAttrs) {
@@ -1479,7 +1513,10 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
"expected a set but found %s: %s",
showType(*vCurrent),
ValuePrinter(state, *vCurrent, errorPrintOptions)
- ).withTrace(pos, "while selecting an attribute").debugThrow();
+ ).addTrace(
+ pos,
+ HintFmt("while selecting '%s' on '%s'", state.symbols[name], partsSoFar())
+ ).debugThrow();
}
// Now that we know this is actually an attrset, try to find an attr