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
This commit is contained in:
Qyriad 2024-06-22 21:59:47 -06:00
parent 6b531e25b9
commit a8e529c47c

View file

@ -1459,12 +1459,46 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
showAttrPath(state, env, attrPath)) showAttrPath(state, env, attrPath))
: nullptr; : nullptr;
for (auto const & currentAttrName : attrPath) { for (auto const & [partIdx, currentAttrName] : enumerate(attrPath)) {
state.nrLookups++; state.nrLookups++;
Symbol const name = getName(currentAttrName, state, env); 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) { if (vCurrent->type() != nAttrs) {
@ -1480,7 +1514,10 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
"expected a set but found %s: %s", "expected a set but found %s: %s",
showType(*vCurrent), showType(*vCurrent),
ValuePrinter(state, *vCurrent, errorPrintOptions) 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 // Now that we know this is actually an attrset, try to find an attr