diff --git a/lix/libcmd/repl.cc b/lix/libcmd/repl.cc index 1152cc1a8..b0663aa7b 100644 --- a/lix/libcmd/repl.cc +++ b/lix/libcmd/repl.cc @@ -988,7 +988,7 @@ Value * NixRepl::replOverlays() auto replInit = evalFile(sourcePath); if (!replInit->isLambda()) { - state.error( + state.errors.make( "Expected `repl-overlays` to be a lambda but found %1%: %2%", showType(*replInit), ValuePrinter(state, *replInit, errorPrintOptions) @@ -999,7 +999,7 @@ Value * NixRepl::replOverlays() if (replInit->lambda.fun->hasFormals() && !replInit->lambda.fun->formals->ellipsis) { - state.error( + state.errors.make( "Expected first argument of %1% to have %2% to allow future versions of Lix to add additional attributes to the argument", "repl-overlays", "..." diff --git a/lix/libexpr/attr-path.cc b/lix/libexpr/attr-path.cc index 23de3923b..a70d5c6cf 100644 --- a/lix/libexpr/attr-path.cc +++ b/lix/libexpr/attr-path.cc @@ -65,7 +65,7 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin if (!attrIndex) { if (v->type() != nAttrs) - state.error( + state.errors.make( "the expression selected by the selection path '%1%' should be a set but is %2%", attrPath, showType(*v)).debugThrow(); @@ -88,7 +88,7 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin else { if (!v->isList()) - state.error( + state.errors.make( "the expression selected by the selection path '%1%' should be a list but is %2%", attrPath, showType(*v)).debugThrow(); diff --git a/lix/libexpr/eval-cache.cc b/lix/libexpr/eval-cache.cc index 1c6dfd18c..b041b8375 100644 --- a/lix/libexpr/eval-cache.cc +++ b/lix/libexpr/eval-cache.cc @@ -506,7 +506,7 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name) // evaluate to see whether 'name' exists } else return nullptr; - //error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); + //errors.make("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); } } @@ -514,7 +514,7 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name) if (v.type() != nAttrs) return nullptr; - //error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); + //errors.make("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); auto attr = v.attrs->get(name); @@ -580,14 +580,14 @@ std::string AttrCursor::getString() debug("using cached string attribute '%s'", getAttrPathStr()); return s->first; } else - root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not a string", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nString && v.type() != nPath) { - root->state.error("'%s' is not a string but %s", getAttrPathStr(), v.type()).debugThrow(); + root->state.errors.make("'%s' is not a string but %s", getAttrPathStr(), v.type()).debugThrow(); } return v.type() == nString ? v.string.s : v.path().to_string(); @@ -623,7 +623,7 @@ string_t AttrCursor::getStringWithContext() return *s; } } else - root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not a string", getAttrPathStr()).debugThrow(); } } @@ -636,7 +636,7 @@ string_t AttrCursor::getStringWithContext() } else if (v.type() == nPath) { return {v.path().to_string(), {}}; } else { - root->state.error("'%s' is not a string but %s", getAttrPathStr(), v.type()).debugThrow(); + root->state.errors.make("'%s' is not a string but %s", getAttrPathStr(), v.type()).debugThrow(); } } @@ -650,14 +650,14 @@ bool AttrCursor::getBool() debug("using cached Boolean attribute '%s'", getAttrPathStr()); return *b; } else - root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nBool) - root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); return v.boolean; } @@ -672,14 +672,14 @@ NixInt AttrCursor::getInt() debug("using cached integer attribute '%s'", getAttrPathStr()); return i->x; } else - root->state.error("'%s' is not an integer", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not an integer", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nInt) - root->state.error("'%s' is not an integer", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not an integer", getAttrPathStr()).debugThrow(); return v.integer; } @@ -694,7 +694,7 @@ std::vector AttrCursor::getListOfStrings() debug("using cached list of strings attribute '%s'", getAttrPathStr()); return *l; } else - root->state.error("'%s' is not a list of strings", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not a list of strings", getAttrPathStr()).debugThrow(); } } @@ -704,7 +704,7 @@ std::vector AttrCursor::getListOfStrings() root->state.forceValue(v, noPos); if (v.type() != nList) - root->state.error("'%s' is not a list", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not a list", getAttrPathStr()).debugThrow(); std::vector res; @@ -727,14 +727,14 @@ std::vector AttrCursor::getAttrs() debug("using cached attrset attribute '%s'", getAttrPathStr()); return *attrs; } else - root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nAttrs) - root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); + root->state.errors.make("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); std::vector attrs; for (auto & attr : *getValue().attrs) diff --git a/lix/libexpr/eval-error.cc b/lix/libexpr/eval-error.cc index 0da05eac1..cf2cdc424 100644 --- a/lix/libexpr/eval-error.cc +++ b/lix/libexpr/eval-error.cc @@ -14,7 +14,7 @@ EvalErrorBuilder EvalErrorBuilder::withExitStatus(unsigned int exitStatus) template EvalErrorBuilder EvalErrorBuilder::atPos(PosIdx pos) && { - error->err.pos = state.positions[pos]; + error->err.pos = positions[pos]; return std::move(*this); } @@ -28,7 +28,7 @@ template EvalErrorBuilder EvalErrorBuilder::withTrace(PosIdx pos, const std::string_view text) && { error->err.traces.push_front( - Trace{.pos = state.positions[pos], .hint = HintFmt(std::string(text))}); + Trace{.pos = positions[pos], .hint = HintFmt(std::string(text))}); return std::move(*this); } @@ -42,9 +42,9 @@ EvalErrorBuilder EvalErrorBuilder::withSuggestions(Suggestions & s) && template EvalErrorBuilder EvalErrorBuilder::withFrame(const Env & env, const Expr & expr) && { - if (state.debug) { - error->frame = state.debug->addTrace(DebugTrace{ - .pos = state.positions[expr.getPos()], + if (debug) { + error->frame = debug->addTrace(DebugTrace{ + .pos = positions[expr.getPos()], .expr = expr, .env = env, .hint = HintFmt("Fake frame for debugging purposes"), @@ -57,7 +57,7 @@ EvalErrorBuilder EvalErrorBuilder::withFrame(const Env & env, const Expr & template EvalErrorBuilder EvalErrorBuilder::addTrace(PosIdx pos, HintFmt hint) && { - error->addTrace(state.positions[pos], hint); + error->addTrace(positions[pos], hint); return std::move(*this); } @@ -67,18 +67,18 @@ EvalErrorBuilder EvalErrorBuilder::addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs) && { - addTrace(state.positions[pos], HintFmt(std::string(formatString), formatArgs...)); + addTrace(positions[pos], HintFmt(std::string(formatString), formatArgs...)); return std::move(*this); } template void EvalErrorBuilder::debugThrow() && { - if (state.debug) { - if (auto last = state.debug->traces().next()) { + if (debug) { + if (auto last = debug->traces().next()) { const Env * env = &(*last)->env; const Expr * expr = &(*last)->expr; - state.debug->onEvalError(error.get(), *env, *expr); + debug->onEvalError(error.get(), *env, *expr); } } diff --git a/lix/libexpr/eval-error.hh b/lix/libexpr/eval-error.hh index 77513943c..2ae775753 100644 --- a/lix/libexpr/eval-error.hh +++ b/lix/libexpr/eval-error.hh @@ -5,9 +5,11 @@ #include "lix/libutil/error.hh" #include "lix/libutil/types.hh" #include "lix/libexpr/pos-idx.hh" +#include "lix/libexpr/pos-table.hh" namespace nix { +struct DebugState; struct DebugTrace; struct Env; struct Expr; @@ -55,18 +57,18 @@ public: template class [[nodiscard]] EvalErrorBuilder final { - friend class EvalState; - - EvalState & state; - - template - explicit EvalErrorBuilder(EvalState & state, const Args &... args) - : state(state), error(make_box_ptr(args...)) - { - } + const PosTable & positions; + DebugState * debug; + box_ptr error; public: - box_ptr error; + template + explicit EvalErrorBuilder(const PosTable & positions, DebugState * debug, const Args &... args) + : positions(positions) + , debug{debug} + , error(make_box_ptr(args...)) + { + } [[gnu::noinline]] EvalErrorBuilder withExitStatus(unsigned int exitStatus) &&; diff --git a/lix/libexpr/eval-inline.hh b/lix/libexpr/eval-inline.hh index 38b5c84d4..882242731 100644 --- a/lix/libexpr/eval-inline.hh +++ b/lix/libexpr/eval-inline.hh @@ -91,7 +91,7 @@ inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view { forceValue(v, pos); if (v.type() != nAttrs) { - error( + errors.make( "expected a set but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) @@ -105,7 +105,7 @@ inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view e { forceValue(v, pos); if (!v.isList()) { - error( + errors.make( "expected a list but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) diff --git a/lix/libexpr/eval.cc b/lix/libexpr/eval.cc index 44d741ce7..991ebb192 100644 --- a/lix/libexpr/eval.cc +++ b/lix/libexpr/eval.cc @@ -299,6 +299,7 @@ EvalState::EvalState( ) : nullptr } + , errors{positions, debug.get()} { countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0"; @@ -750,7 +751,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) return j->value; } if (!fromWith->parentWith) - error("undefined variable '%1%'", symbols[var.name]).atPos(var.pos).withFrame(*env, var).debugThrow(); + errors.make("undefined variable '%1%'", symbols[var.name]).atPos(var.pos).withFrame(*env, var).debugThrow(); for (size_t l = fromWith->prevWith; l; --l, env = env->up) ; fromWith = fromWith->parentWith; } @@ -981,7 +982,7 @@ inline bool EvalState::evalBool(Env & env, Expr & e, const PosIdx pos, std::stri Value v; e.eval(*this, env, v); if (v.type() != nBool) - error( + errors.make( "expected a Boolean but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) @@ -999,7 +1000,7 @@ inline void EvalState::evalAttrs(Env & env, Expr & e, Value & v, const PosIdx po try { e.eval(*this, env, v); if (v.type() != nAttrs) - error( + errors.make( "expected a set but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) @@ -1131,7 +1132,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) auto nameSym = state.symbols.create(nameVal.string.s); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) - state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow(); + state.errors.make("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow(); i.valueExpr->setName(nameSym); /* Keep sorted order so find can catch duplicates */ @@ -1306,7 +1307,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) } // Otherwise, we must type error. - state.error( + state.errors.make( "expected a set but found %s: %s", showType(*vCurrent), ValuePrinter(state, *vCurrent, errorPrintOptions) @@ -1333,7 +1334,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) allAttrNames.insert(state.symbols[attr.name]); } auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]); - state.error("attribute '%s' missing", state.symbols[name]) + state.errors.make("attribute '%s' missing", state.symbols[name]) .atPos(pos) .withSuggestions(suggestions) .withFrame(env, *this) @@ -1469,7 +1470,7 @@ FormalsMatch matchupFormals(EvalState & state, Env & env, Displacement & displ, void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos) { if (callDepth > evalSettings.maxCallDepth) - error("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow(); + errors.make("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow(); CallDepth _level(callDepth); auto trace = evalSettings.traceFunctionCalls @@ -1531,7 +1532,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & ); for (auto const & missingArg : formalsMatch.missing) { auto const missing = symbols[missingArg]; - error("function '%s' called without required argument '%s'", lambda.getName(symbols), missing) + errors.make("function '%s' called without required argument '%s'", lambda.getName(symbols), missing) .atPos(lambda.pos) .withTrace(pos, "from call site") .withFrame(*fun.lambda.env, lambda) @@ -1544,7 +1545,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & formalNames.insert(symbols[formal.name]); } auto sug = Suggestions::bestMatches(formalNames, unex); - error("function '%s' called with unexpected argument '%s'", lambda.getName(symbols), unex) + errors.make("function '%s' called with unexpected argument '%s'", lambda.getName(symbols), unex) .atPos(lambda.pos) .withTrace(pos, "from call site") .withSuggestions(sug) @@ -1685,7 +1686,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } else - error( + errors.make( "attempt to call something which is not a function but %1%: %2%", showType(vCur), ValuePrinter(*this, vCur, errorPrintOptions)) @@ -1772,7 +1773,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) if (j != args.end()) { attrs.insert(*j); } else if (!i.def) { - error(R"(cannot evaluate a function that has an argument without a value ('%1%') + errors.make(R"(cannot evaluate a function that has an argument without a value ('%1%') Nix attempted to evaluate a function as a top level expression; in this case it must have its arguments supplied either by default values, or passed explicitly with '--arg' or '--argstr'. See @@ -1808,7 +1809,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v) if (!state.evalBool(env, *cond, pos, "in the condition of the assert statement")) { std::ostringstream out; cond->show(state.symbols, out); - state.error("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow(); + state.errors.make("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow(); } body->eval(state, env, v); } @@ -1983,7 +1984,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) if (auto checked = newN.valueChecked(); checked.has_value()) { n = NixInt(*checked); } else { - state.error("integer overflow in adding %1% + %2%", n, vTmp.integer).atPos(i_pos).debugThrow(); + state.errors.make("integer overflow in adding %1% + %2%", n, vTmp.integer).atPos(i_pos).debugThrow(); } } else if (vTmp.type() == nFloat) { // Upgrade the type from int to float; @@ -1991,14 +1992,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) nf = n.value; nf += vTmp.fpoint; } else - state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); + state.errors.make("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { nf += vTmp.integer.value; } else if (vTmp.type() == nFloat) { nf += vTmp.fpoint; } else - state.error("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); + state.errors.make("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else { if (s.empty()) s.reserve(es.size()); /* skip canonization of first path, which would only be not @@ -2020,7 +2021,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) v.mkFloat(nf); else if (firstType == nPath) { if (!context.empty()) - state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow(); + state.errors.make("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow(); v.mkPath(CanonPath(canonPath(str()))); } else v.mkStringMove(c_str(), context); @@ -2035,7 +2036,7 @@ void ExprPos::eval(EvalState & state, Env & env, Value & v) void ExprBlackHole::eval(EvalState & state, Env & env, Value & v) { - state.error("infinite recursion encountered") + state.errors.make("infinite recursion encountered") .atPos(v.determinePos(noPos)) .debugThrow(); } @@ -2099,7 +2100,7 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt try { forceValue(v, pos); if (v.type() != nInt) - error( + errors.make( "expected an integer but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) @@ -2121,7 +2122,7 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err if (v.type() == nInt) return v.integer.value; else if (v.type() != nFloat) - error( + errors.make( "expected a float but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) @@ -2139,7 +2140,7 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx try { forceValue(v, pos); if (v.type() != nBool) - error( + errors.make( "expected a Boolean but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) @@ -2165,7 +2166,7 @@ void EvalState::forceFunction(Value & v, const PosIdx pos, std::string_view erro try { forceValue(v, pos); if (v.type() != nFunction && !isFunctor(v)) - error( + errors.make( "expected a function but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) @@ -2182,7 +2183,7 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string try { forceValue(v, pos); if (v.type() != nString) - error( + errors.make( "expected a string but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) @@ -2215,7 +2216,7 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s { auto s = forceString(v, pos, errorCtx); if (v.string.context) { - error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow(); + errors.make("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow(); } return s; } @@ -2280,7 +2281,7 @@ BackedStringView EvalState::coerceToString( return std::move(*maybeString); auto i = v.attrs->find(s.outPath); if (i == v.attrs->end()) { - error( + errors.make( "cannot coerce %1% to a string: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) @@ -2330,7 +2331,7 @@ BackedStringView EvalState::coerceToString( } } - error("cannot coerce %1% to a string: %2%", + errors.make("cannot coerce %1% to a string: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) ) @@ -2342,7 +2343,7 @@ BackedStringView EvalState::coerceToString( StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePath & path) { if (nix::isDerivation(path.path.abs())) - error("file names are not allowed to end in '%1%'", drvExtension).debugThrow(); + errors.make("file names are not allowed to end in '%1%'", drvExtension).debugThrow(); auto i = srcToStore.find(path); @@ -2367,7 +2368,7 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext { auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned(); if (path == "" || path[0] != '/') - error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow(); + errors.make("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow(); return CanonPath(path); } @@ -2377,7 +2378,7 @@ StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, NixStringCon auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) return *storePath; - error("path '%1%' is not in the Nix store", path).withTrace(pos, errorCtx).debugThrow(); + errors.make("path '%1%' is not in the Nix store", path).withTrace(pos, errorCtx).debugThrow(); } @@ -2387,7 +2388,7 @@ std::pair EvalState::coerceToSingleDerivedP auto s = forceString(v, context, pos, errorCtx); auto csize = context.size(); if (csize != 1) - error( + errors.make( "string '%s' has %d entries in its context. It should only have exactly one entry", s, csize) .withTrace(pos, errorCtx).debugThrow(); @@ -2396,7 +2397,7 @@ std::pair EvalState::coerceToSingleDerivedP return std::move(o); }, [&](NixStringContextElem::DrvDeep &&) -> SingleDerivedPath { - error( + errors.make( "string '%s' has a context which refers to a complete source and binary closure. This is not supported at this time", s).withTrace(pos, errorCtx).debugThrow(); }, @@ -2421,13 +2422,13 @@ SingleDerivedPath EvalState::coerceToSingleDerivedPath(const PosIdx pos, Value & error message. */ std::visit(overloaded { [&](const SingleDerivedPath::Opaque & o) { - error( + errors.make( "path string '%s' has context with the different path '%s'", s, sExpected) .withTrace(pos, errorCtx).debugThrow(); }, [&](const SingleDerivedPath::Built & b) { - error( + errors.make( "string '%s' has context with the output '%s' from derivation '%s', but the string is not the right placeholder for this derivation output. It should be '%s'", s, b.output, b.drvPath->to_string(*store), sExpected) .withTrace(pos, errorCtx).debugThrow(); @@ -2512,7 +2513,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v case nThunk: // Must not be left by forceValue default: - error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow(); + errors.make("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow(); } } @@ -2755,7 +2756,7 @@ SourcePath EvalState::findFile(const SearchPath & searchPath, const std::string_ if (path.starts_with("nix/")) return CanonPath(concatStrings(corepkgsPrefix, path.substr(4))); - error( + errors.make( evalSettings.pureEval ? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)" : "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)", @@ -2817,7 +2818,7 @@ std::optional EvalState::resolveSearchPathPath(const SearchPath::Pa std::string ExternalValueBase::coerceToString(EvalState & state, const PosIdx & pos, NixStringContext & context, bool copyMore, bool copyToStore) const { - state.error( + state.errors.make( "cannot coerce %1% to a string: %2%", showType(), *this ).atPos(pos).debugThrow(); } diff --git a/lix/libexpr/eval.hh b/lix/libexpr/eval.hh index e35ec35b1..298f3ddba 100644 --- a/lix/libexpr/eval.hh +++ b/lix/libexpr/eval.hh @@ -348,6 +348,18 @@ struct EvalRuntimeCaches std::map> fileEval; }; +struct EvalErrorContext +{ + const PosTable & positions; + DebugState * debug; + + template + [[gnu::noinline]] + EvalErrorBuilder make(const Args & ... args) { + return EvalErrorBuilder(positions, debug, args...); + } +}; + class EvalState { @@ -389,12 +401,7 @@ public: const ref buildStore; std::unique_ptr debug; - - template - [[gnu::noinline]] - EvalErrorBuilder error(const Args & ... args) { - return EvalErrorBuilder(*this, args...); - } + EvalErrorContext errors; private: diff --git a/lix/libexpr/flake/flake.cc b/lix/libexpr/flake/flake.cc index 202a05ace..6854cf738 100644 --- a/lix/libexpr/flake/flake.cc +++ b/lix/libexpr/flake/flake.cc @@ -142,14 +142,14 @@ static FlakeInput parseFlakeInput(EvalState & state, auto intValue = attr.value->integer.value; if (intValue < 0) { - state.error("negative value given for flake input attribute %1%: %2%", state.symbols[attr.name], intValue).debugThrow(); + state.errors.make("negative value given for flake input attribute %1%: %2%", state.symbols[attr.name], intValue).debugThrow(); } uint64_t asUnsigned = intValue; attrs.emplace(state.symbols[attr.name], asUnsigned); break; } default: - state.error("flake input attribute '%s' is %s while a string, Boolean, or integer is expected", + state.errors.make("flake input attribute '%s' is %s while a string, Boolean, or integer is expected", state.symbols[attr.name], showType(*attr.value)).debugThrow(); } #pragma GCC diagnostic pop @@ -247,7 +247,7 @@ static Flake getFlake( // Enforce that 'flake.nix' is a direct attrset, not a computation. if (!(dynamic_cast(&flakeExpr))) { - state.error("file '%s' must be an attribute set", resolvedFlakeFile).debugThrow(); + state.errors.make("file '%s' must be an attribute set", resolvedFlakeFile).debugThrow(); } Value vInfo; @@ -307,14 +307,14 @@ static Flake getFlake( std::vector ss; for (auto elem : setting.value->listItems()) { if (elem->type() != nString) - state.error("list element in flake configuration setting '%s' is %s while a string is expected", + state.errors.make("list element in flake configuration setting '%s' is %s while a string is expected", state.symbols[setting.name], showType(*setting.value)).debugThrow(); ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos, "")); } flake.config.settings.emplace(state.symbols[setting.name], ss); } else - state.error("flake configuration setting '%s' is %s", + state.errors.make("flake configuration setting '%s' is %s", state.symbols[setting.name], showType(*setting.value)).debugThrow(); } } @@ -858,7 +858,7 @@ void prim_flakeRefToString( auto intValue = attr.value->integer.value; if (intValue < 0) { - state.error("negative value given for flake ref attr %1%: %2%", state.symbols[attr.name], intValue).atPos(pos).debugThrow(); + state.errors.make("negative value given for flake ref attr %1%: %2%", state.symbols[attr.name], intValue).atPos(pos).debugThrow(); } uint64_t asUnsigned = intValue; @@ -870,7 +870,7 @@ void prim_flakeRefToString( attrs.emplace(state.symbols[attr.name], std::string(attr.value->str())); } else { - state.error( + state.errors.make( "flake reference attribute sets may only contain integers, Booleans, " "and strings, but attribute '%s' is %s", state.symbols[attr.name], diff --git a/lix/libexpr/get-drvs.cc b/lix/libexpr/get-drvs.cc index 91ae4cfc6..59c30985c 100644 --- a/lix/libexpr/get-drvs.cc +++ b/lix/libexpr/get-drvs.cc @@ -50,7 +50,7 @@ std::string DrvInfo::queryName() { if (name == "" && attrs) { auto i = attrs->find(state->s.name); - if (i == attrs->end()) state->error("derivation name missing").debugThrow(); + if (i == attrs->end()) state->errors.make("derivation name missing").debugThrow(); name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation"); } return name; @@ -446,7 +446,7 @@ static void getDerivations(EvalState & state, Value & vIn, return; } else if (v.type() != nAttrs) { - state.error( + state.errors.make( "expression was expected to be a derivation or collection of derivations, but instead was %s", showType(v.type(), true) ).debugThrow(); diff --git a/lix/libexpr/nixexpr.cc b/lix/libexpr/nixexpr.cc index 4b051ff5e..3c017e77d 100644 --- a/lix/libexpr/nixexpr.cc +++ b/lix/libexpr/nixexpr.cc @@ -364,7 +364,7 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr & enclosing `with'. If there is no `with', then we can issue an "undefined variable" error now. */ if (withLevel == -1) - es.error( + es.errors.make( "undefined variable '%1%'", es.symbols[name] ).atPos(pos).debugThrow(); diff --git a/lix/libexpr/primops.cc b/lix/libexpr/primops.cc index aba5d43ec..bfe511020 100644 --- a/lix/libexpr/primops.cc +++ b/lix/libexpr/primops.cc @@ -47,7 +47,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context) for (auto & c : context) { auto ensureValid = [&](const StorePath & p) { if (!store->isValidPath(p)) - error(store->printStorePath(p)).debugThrow(); + errors.make(store->printStorePath(p)).debugThrow(); }; std::visit(overloaded { [&](const NixStringContextElem::Built & b) { @@ -74,7 +74,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context) if (drvs.empty()) return {}; if (!evalSettings.enableImportFromDerivation) - error( + errors.make( "cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled", drvs.begin()->to_string(*store) ).debugThrow(); @@ -285,16 +285,16 @@ void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Valu void *handle = dlopen(path.path.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) - state.error("could not open '%1%': %2%", path, dlerror()).debugThrow(); + state.errors.make("could not open '%1%': %2%", path, dlerror()).debugThrow(); dlerror(); ValueInitializer func = reinterpret_cast(dlsym(handle, sym.c_str())); if(!func) { char *message = dlerror(); if (message) - state.error("could not load symbol '%1%' from '%2%': %3%", sym, path, message).debugThrow(); + state.errors.make("could not load symbol '%1%' from '%2%': %3%", sym, path, message).debugThrow(); else - state.error("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected", sym, path).debugThrow(); + state.errors.make("symbol '%1%' from '%2%' resolved to NULL when a function pointer was expected", sym, path).debugThrow(); } (func)(state, v); @@ -310,7 +310,7 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v) auto elems = args[0]->listElems(); auto count = args[0]->listSize(); if (count == 0) - state.error("at least one argument to 'exec' required").atPos(pos).debugThrow(); + state.errors.make("at least one argument to 'exec' required").atPos(pos).debugThrow(); NixStringContext context; auto program = state.coerceToString(pos, *elems[0], context, "while evaluating the first element of the argument passed to builtins.exec", @@ -452,7 +452,7 @@ struct CompareValues if (v1->type() == nInt && v2->type() == nFloat) return v1->integer.value < v2->fpoint; if (v1->type() != v2->type()) - state.error("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow(); + state.errors.make("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow(); // Allow selecting a subset of enum values #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" @@ -477,7 +477,7 @@ struct CompareValues } } default: - state.error("cannot compare %s with %s; values of that type are incomparable", showType(*v1), showType(*v2)).debugThrow(); + state.errors.make("cannot compare %s with %s; values of that type are incomparable", showType(*v1), showType(*v2)).debugThrow(); #pragma GCC diagnostic pop } } catch (Error & e) { @@ -503,7 +503,7 @@ static Bindings::iterator getAttr( { Bindings::iterator value = attrSet->find(attrSym); if (value == attrSet->end()) { - state.error("attribute '%s' missing", state.symbols[attrSym]).withTrace(noPos, errorCtx).debugThrow(); + state.errors.make("attribute '%s' missing", state.symbols[attrSym]).withTrace(noPos, errorCtx).debugThrow(); } return value; } @@ -591,7 +591,7 @@ static void prim_abort(EvalState & state, const PosIdx pos, Value * * args, Valu NixStringContext context; auto s = state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtins.abort").toOwned(); - state.error("evaluation aborted with the following error message: '%1%'", s).debugThrow(); + state.errors.make("evaluation aborted with the following error message: '%1%'", s).debugThrow(); } static void prim_throw(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -599,7 +599,7 @@ static void prim_throw(EvalState & state, const PosIdx pos, Value * * args, Valu NixStringContext context; auto s = state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtin.throw").toOwned(); - state.error(s).debugThrow(); + state.errors.make(s).debugThrow(); } static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) @@ -827,7 +827,7 @@ drvName, Bindings * attrs, Value & v) experimentalFeatureSettings.require(Xp::DynamicDerivations); ingestionMethod = TextIngestionMethod {}; } else - state.error( + state.errors.make( "invalid value '%s' for 'outputHashMode' attribute", s ).atPos(v).debugThrow(); }; @@ -836,7 +836,7 @@ drvName, Bindings * attrs, Value & v) outputs.clear(); for (auto & j : ss) { if (outputs.find(j) != outputs.end()) - state.error("duplicate derivation output '%1%'", j) + state.errors.make("duplicate derivation output '%1%'", j) .atPos(v) .debugThrow(); /* !!! Check whether j is a valid attribute @@ -845,13 +845,13 @@ drvName, Bindings * attrs, Value & v) then we'd have an attribute ‘drvPath’ in the resulting set. */ if (j == "drv") - state.error("invalid derivation output name 'drv'") + state.errors.make("invalid derivation output name 'drv'") .atPos(v) .debugThrow(); outputs.insert(j); } if (outputs.empty()) - state.error("derivation cannot have an empty set of outputs") + state.errors.make("derivation cannot have an empty set of outputs") .atPos(v) .debugThrow(); }; @@ -989,12 +989,12 @@ drvName, Bindings * attrs, Value & v) /* Do we have all required attributes? */ if (drv.builder == "") - state.error("required attribute 'builder' missing") + state.errors.make("required attribute 'builder' missing") .atPos(v) .debugThrow(); if (drv.platform == "") - state.error("required attribute 'system' missing") + state.errors.make("required attribute 'system' missing") .atPos(v) .debugThrow(); @@ -1004,7 +1004,7 @@ drvName, Bindings * attrs, Value & v) outputs.size() == 1 && *(outputs.begin()) == "out")) { - state.error( + state.errors.make( "derivation names are allowed to end in '%s' only if they produce a single derivation file", drvExtension ).atPos(v).debugThrow(); @@ -1016,7 +1016,7 @@ drvName, Bindings * attrs, Value & v) Ignore `__contentAddressed` because fixed output derivations are already content addressed. */ if (outputs.size() != 1 || *(outputs.begin()) != "out") - state.error( + state.errors.make( "multiple outputs are not supported in fixed-output derivations" ).atPos(v).debugThrow(); @@ -1037,7 +1037,7 @@ drvName, Bindings * attrs, Value & v) else if (contentAddressed || isImpure) { if (contentAddressed && isImpure) - state.error("derivation cannot be both content-addressed and impure") + state.errors.make("derivation cannot be both content-addressed and impure") .atPos(v).debugThrow(); auto ht = parseHashTypeOpt(outputHashAlgo).value_or(HashType::SHA256); @@ -1079,7 +1079,7 @@ drvName, Bindings * attrs, Value & v) for (auto & i : outputs) { auto h = get(hashModulo.hashes, i); if (!h) - state.error( + state.errors.make( "derivation produced no hash for output '%s'", i ).atPos(v).debugThrow(); @@ -1167,7 +1167,7 @@ static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Val static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v) { if (evalSettings.pureEval) - state.error( + state.errors.make( "'%s' is not allowed in pure evaluation mode", "builtins.storePath" ).atPos(pos).debugThrow(); @@ -1180,7 +1180,7 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, if (!state.store->isStorePath(path.abs())) path = CanonPath(canonPath(path.abs(), true)); if (!state.store->isInStore(path.abs())) - state.error("path '%1%' is not in the Nix store", path) + state.errors.make("path '%1%' is not in the Nix store", path) .atPos(pos).debugThrow(); auto path2 = state.store->toStorePath(path.abs()).first; if (!settings.readOnlyMode) @@ -1257,7 +1257,7 @@ static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, V auto path = realisePath(state, pos, *args[0]); auto s = path.readFile(); if (s.find((char) 0) != std::string::npos) - state.error( + state.errors.make( "the contents of the file '%1%' cannot be represented as a Nix string", path ).atPos(pos).debugThrow(); @@ -1308,7 +1308,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V auto rewrites = state.realiseContext(context); path = rewriteStrings(path, rewrites); } catch (InvalidPathError & e) { - state.error( + state.errors.make( "cannot find '%1%', since path '%2%' is not valid", path, e.path @@ -1332,7 +1332,7 @@ static void prim_hashFile(EvalState & state, const PosIdx pos, Value * * args, V auto type = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hashFile"); std::optional ht = parseHashType(type); if (!ht) - state.error("unknown hash type '%1%'", type).atPos(pos).debugThrow(); + state.errors.make("unknown hash type '%1%'", type).atPos(pos).debugThrow(); auto path = realisePath(state, pos, *args[1]); @@ -1461,7 +1461,7 @@ static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Val if (auto p = std::get_if(&c.raw)) refs.insert(p->path); else - state.error( + state.errors.make( "files created by %1% may not reference derivations, but %2% references %3%", "builtins.toFile", name, @@ -1548,7 +1548,7 @@ static void addPath( auto dstPath = fetchToStore( *state.store, CanonPath(path), name, method, &filter, state.repair); if (expectedHash && expectedStorePath != dstPath) - state.error( + state.errors.make( "store path mismatch in (possibly filtered) path added from '%s'", path ).atPos(pos).debugThrow(); @@ -1595,13 +1595,13 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), HashType::SHA256); else - state.error( + state.errors.make( "unsupported argument '%1%' to 'addPath'", state.symbols[attr.name] ).atPos(attr.pos).debugThrow(); } if (!path) - state.error( + state.errors.make( "missing required 'path' attribute in the first argument to builtins.path" ).atPos(pos).debugThrow(); if (name.empty()) @@ -1908,7 +1908,7 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg return; } if (!args[0]->isLambda()) - state.error("'functionArgs' requires a function").atPos(pos).debugThrow(); + state.errors.make("'functionArgs' requires a function").atPos(pos).debugThrow(); if (!args[0]->lambda.fun->hasFormals()) { v.mkAttrs(&Bindings::EMPTY); @@ -2005,7 +2005,7 @@ static void elemAt(EvalState & state, const PosIdx pos, Value & list, int n, Val { state.forceList(list, pos, "while evaluating the first argument passed to builtins.elemAt"); if (n < 0 || (unsigned int) n >= list.listSize()) - state.error( + state.errors.make( "list index %1% is out of bounds", n ).atPos(pos).debugThrow(); @@ -2033,7 +2033,7 @@ static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value { state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.tail"); if (args[0]->listSize() == 0) - state.error("'tail' called on an empty list").atPos(pos).debugThrow(); + state.errors.make("'tail' called on an empty list").atPos(pos).debugThrow(); v = state.mem.newList(args[0]->listSize() - 1); for (unsigned int n = 0; n < v.listSize(); ++n) @@ -2180,7 +2180,7 @@ static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Va auto len_ = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.genList").value; if (len_ < 0) - state.error("cannot create list of size %1%", len_).atPos(pos).debugThrow(); + state.errors.make("cannot create list of size %1%", len_).atPos(pos).debugThrow(); size_t len = len_; @@ -2353,7 +2353,7 @@ static void prim_add(EvalState & state, const PosIdx pos, Value * * args, Value if (auto result = result_.valueChecked(); result.has_value()) { v.mkInt(*result); } else { - state.error("integer overflow in adding %1% + %2%", i1, i2).atPos(pos).debugThrow(); + state.errors.make("integer overflow in adding %1% + %2%", i1, i2).atPos(pos).debugThrow(); } } } @@ -2374,7 +2374,7 @@ static void prim_sub(EvalState & state, const PosIdx pos, Value * * args, Value if (auto result = result_.valueChecked(); result.has_value()) { v.mkInt(*result); } else { - state.error("integer overflow in subtracting %1% - %2%", i1, i2).atPos(pos).debugThrow(); + state.errors.make("integer overflow in subtracting %1% - %2%", i1, i2).atPos(pos).debugThrow(); } } } @@ -2395,7 +2395,7 @@ static void prim_mul(EvalState & state, const PosIdx pos, Value * * args, Value if (auto result = result_.valueChecked(); result.has_value()) { v.mkInt(*result); } else { - state.error("integer overflow in multiplying %1% * %2%", i1, i2).atPos(pos).debugThrow(); + state.errors.make("integer overflow in multiplying %1% * %2%", i1, i2).atPos(pos).debugThrow(); } } } @@ -2407,7 +2407,7 @@ static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value NixFloat f2 = state.forceFloat(*args[1], pos, "while evaluating the second operand of the division"); if (f2 == 0) - state.error("division by zero").atPos(pos).debugThrow(); + state.errors.make("division by zero").atPos(pos).debugThrow(); if (args[0]->type() == nFloat || args[1]->type() == nFloat) { v.mkFloat(state.forceFloat(*args[0], pos, "while evaluating the first operand of the division") / f2); @@ -2419,7 +2419,7 @@ static void prim_div(EvalState & state, const PosIdx pos, Value * * args, Value if (auto result = result_.valueChecked(); result.has_value()) { v.mkInt(*result); } else { - state.error("integer overflow in dividing %1% / %2%", i1, i2).atPos(pos).debugThrow(); + state.errors.make("integer overflow in dividing %1% / %2%", i1, i2).atPos(pos).debugThrow(); } } } @@ -2483,7 +2483,7 @@ static void prim_substring(EvalState & state, const PosIdx pos, Value * * args, NixInt::Inner start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring").value; if (start < 0) - state.error("negative start position in 'substring'").atPos(pos).debugThrow(); + state.errors.make("negative start position in 'substring'").atPos(pos).debugThrow(); NixInt::Inner len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring").value; @@ -2523,7 +2523,7 @@ static void prim_hashString(EvalState & state, const PosIdx pos, Value * * args, auto type = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hashString"); std::optional ht = parseHashType(type); if (!ht) - state.error("unknown hash algorithm '%1%'", type).atPos(pos).debugThrow(); + state.errors.make("unknown hash algorithm '%1%'", type).atPos(pos).debugThrow(); NixStringContext context; // discarded auto s = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.hashString"); @@ -2585,11 +2585,11 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v) } catch (std::regex_error & e) { if (e.code() == std::regex_constants::error_space) { // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ - state.error("memory limit exceeded by regular expression '%s'", re) + state.errors.make("memory limit exceeded by regular expression '%s'", re) .atPos(pos) .debugThrow(); } else - state.error("invalid regular expression '%s'", re) + state.errors.make("invalid regular expression '%s'", re) .atPos(pos) .debugThrow(); } @@ -2651,11 +2651,11 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) } catch (std::regex_error & e) { if (e.code() == std::regex_constants::error_space) { // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ - state.error("memory limit exceeded by regular expression '%s'", re) + state.errors.make("memory limit exceeded by regular expression '%s'", re) .atPos(pos) .debugThrow(); } else - state.error("invalid regular expression '%s'", re) + state.errors.make("invalid regular expression '%s'", re) .atPos(pos) .debugThrow(); } @@ -2685,7 +2685,7 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.replaceStrings"); state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.replaceStrings"); if (args[0]->listSize() != args[1]->listSize()) - state.error( + state.errors.make( "'from' and 'to' arguments passed to builtins.replaceStrings have different lengths" ).atPos(pos).debugThrow(); diff --git a/lix/libexpr/primops/context.cc b/lix/libexpr/primops/context.cc index 9e8fe79e4..a0a59db7a 100644 --- a/lix/libexpr/primops/context.cc +++ b/lix/libexpr/primops/context.cc @@ -56,7 +56,7 @@ void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * auto contextSize = context.size(); if (contextSize != 1) { - state.error( + state.errors.make( "context of string '%s' must have exactly one element, but has %d", *s, contextSize @@ -66,7 +66,7 @@ void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * (NixStringContextElem { std::visit(overloaded { [&](const NixStringContextElem::Opaque & c) -> NixStringContextElem::DrvDeep { if (!c.path.isDerivation()) { - state.error( + state.errors.make( "path '%s' is not a derivation", state.store->printStorePath(c.path) ).atPos(pos).debugThrow(); @@ -76,7 +76,7 @@ void prim_addDrvOutputDependencies(EvalState & state, const PosIdx pos, Value * }; }, [&](const NixStringContextElem::Built & c) -> NixStringContextElem::DrvDeep { - state.error( + state.errors.make( "`addDrvOutputDependencies` can only act on derivations, not on a derivation output such as '%1%'", c.output ).atPos(pos).debugThrow(); @@ -176,7 +176,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar for (auto & i : *args[1]->attrs) { const auto & name = state.symbols[i.name]; if (!state.store->isStorePath(name)) - state.error( + state.errors.make( "context key '%s' is not a store path", name ).atPos(i.pos).debugThrow(); @@ -196,7 +196,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar if (iter != i.value->attrs->end()) { if (state.forceBool(*iter->value, iter->pos, "while evaluating the `allOutputs` attribute of a string context")) { if (!isDerivation(name)) { - state.error( + state.errors.make( "tried to add all-outputs context of %s, which is not a derivation, to a string", name ).atPos(i.pos).debugThrow(); @@ -211,7 +211,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar if (iter != i.value->attrs->end()) { state.forceList(*iter->value, iter->pos, "while evaluating the `outputs` attribute of a string context"); if (iter->value->listSize() && !isDerivation(name)) { - state.error( + state.errors.make( "tried to add derivation output context of %s, which is not a derivation, to a string", name ).atPos(i.pos).debugThrow(); diff --git a/lix/libexpr/primops/fetchMercurial.cc b/lix/libexpr/primops/fetchMercurial.cc index 31c49750b..518d16307 100644 --- a/lix/libexpr/primops/fetchMercurial.cc +++ b/lix/libexpr/primops/fetchMercurial.cc @@ -38,11 +38,11 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a else if (n == "name") name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial"); else - state.error("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]).atPos(attr.pos).debugThrow(); + state.errors.make("unsupported argument '%s' to 'fetchMercurial'", state.symbols[attr.name]).atPos(attr.pos).debugThrow(); } if (url.empty()) - state.error("'url' argument required").atPos(pos).debugThrow(); + state.errors.make("'url' argument required").atPos(pos).debugThrow(); } else url = state.coerceToString(pos, *args[0], context, diff --git a/lix/libexpr/primops/fetchTree.cc b/lix/libexpr/primops/fetchTree.cc index cb91930cf..bbef00f72 100644 --- a/lix/libexpr/primops/fetchTree.cc +++ b/lix/libexpr/primops/fetchTree.cc @@ -124,12 +124,12 @@ static void fetchTree( if (auto aType = args[0]->attrs->get(state.s.type)) { if (type) - state.error( + state.errors.make( "unexpected attribute 'type'" ).atPos(pos).debugThrow(); type = state.forceStringNoCtx(*aType->value, aType->pos, "while evaluating the `type` attribute passed to builtins.fetchTree"); } else if (!type) - state.error( + state.errors.make( "attribute 'type' is missing in call to 'fetchTree'" ).atPos(pos).debugThrow(); @@ -153,19 +153,19 @@ static void fetchTree( auto intValue = attr.value->integer.value; if (intValue < 0) { - state.error("negative value given for fetchTree attr %1%: %2%", state.symbols[attr.name], intValue).atPos(pos).debugThrow(); + state.errors.make("negative value given for fetchTree attr %1%: %2%", state.symbols[attr.name], intValue).atPos(pos).debugThrow(); } unsigned long asUnsigned = intValue; attrs.emplace(state.symbols[attr.name], asUnsigned); } else - state.error("fetchTree argument '%s' is %s while a string, Boolean or integer is expected", + state.errors.make("fetchTree argument '%s' is %s while a string, Boolean or integer is expected", state.symbols[attr.name], showType(*attr.value)).debugThrow(); } if (!params.allowNameArgument) if (auto nameIter = attrs.find("name"); nameIter != attrs.end()) - state.error( + state.errors.make( "attribute 'name' isn’t supported in call to 'fetchTree'" ).atPos(pos).debugThrow(); @@ -189,7 +189,7 @@ static void fetchTree( input = lookupInRegistries(state.store, input).first; if (evalSettings.pureEval && !input.isLocked()) { - state.error("in pure evaluation mode, 'fetchTree' requires a locked input").atPos(pos).debugThrow(); + state.errors.make("in pure evaluation mode, 'fetchTree' requires a locked input").atPos(pos).debugThrow(); } auto [tree, input2] = input.fetch(state.store); @@ -231,12 +231,12 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v else if (n == "name") name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch"); else - state.error("unsupported argument '%s' to '%s'", n, who) + state.errors.make("unsupported argument '%s' to '%s'", n, who) .atPos(pos).debugThrow(); } if (!url) - state.error( + state.errors.make( "'url' argument required").atPos(pos).debugThrow(); } else url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch"); @@ -250,7 +250,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v name = baseNameOf(*url); if (evalSettings.pureEval && !expectedHash) - state.error("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow(); + state.errors.make("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow(); // early exit if pinned and already in the store if (expectedHash && expectedHash->type == HashType::SHA256) { @@ -280,7 +280,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v ? state.store->queryPathInfo(storePath)->narHash : hashFile(HashType::SHA256, state.store->toRealPath(storePath)); if (hash != *expectedHash) { - state.error( + state.errors.make( "hash mismatch in file downloaded from '%s':\n specified: %s\n got: %s", *url, expectedHash->to_string(Base::Base32, true), diff --git a/lix/libexpr/primops/fromTOML.cc b/lix/libexpr/primops/fromTOML.cc index 83f9052c4..2a66fc95b 100644 --- a/lix/libexpr/primops/fromTOML.cc +++ b/lix/libexpr/primops/fromTOML.cc @@ -83,7 +83,7 @@ void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, Value & try { visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */)); } catch (std::exception & e) { // TODO: toml::syntax_error - state.error("while parsing TOML: %s", e.what()).atPos(pos).debugThrow(); + state.errors.make("while parsing TOML: %s", e.what()).atPos(pos).debugThrow(); } } diff --git a/lix/libexpr/value-to-json.cc b/lix/libexpr/value-to-json.cc index b6b7caa03..670de061c 100644 --- a/lix/libexpr/value-to-json.cc +++ b/lix/libexpr/value-to-json.cc @@ -99,7 +99,7 @@ json printValueAsJSON(EvalState & state, bool strict, case nThunk: case nFunction: - state.error( + state.errors.make( "cannot convert %1% to JSON", showType(v) ) @@ -118,7 +118,7 @@ void printValueAsJSON(EvalState & state, bool strict, json ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, NixStringContext & context, bool copyToStore) const { - state.error("cannot convert %1% to JSON", showType()) + state.errors.make("cannot convert %1% to JSON", showType()) .debugThrow(); } diff --git a/lix/libutil/error.cc b/lix/libutil/error.cc index cd5382d66..db05d6319 100644 --- a/lix/libutil/error.cc +++ b/lix/libutil/error.cc @@ -330,7 +330,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s * try { * e->eval(*this, env, v); * if (v.type() != nAttrs) - * error("expected a set but found %1%", v); + * errors.make("expected a set but found %1%", v); * } catch (Error & e) { * e.addTrace(pos, errorCtx); * throw; @@ -344,7 +344,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s * e->eval(*this, env, v); * try { * if (v.type() != nAttrs) - * error("expected a set but found %1%", v); + * errors.make("expected a set but found %1%", v); * } catch (Error & e) { * e.addTrace(pos, errorCtx); * throw; diff --git a/lix/nix/eval.cc b/lix/nix/eval.cc index 906e3e57f..c6f1f2128 100644 --- a/lix/nix/eval.cc +++ b/lix/nix/eval.cc @@ -106,7 +106,7 @@ struct CmdEval : MixJSON, InstallableCommand, MixReadOnlyOption } } else - state->error("value at '%s' is not a string or an attribute set", state->positions[pos]).debugThrow(); + state->errors.make("value at '%s' is not a string or an attribute set", state->positions[pos]).debugThrow(); }; recurse(*v, pos, *writeTo); diff --git a/lix/nix/flake.cc b/lix/nix/flake.cc index 18383247f..e8a239c39 100644 --- a/lix/nix/flake.cc +++ b/lix/nix/flake.cc @@ -868,7 +868,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand auto templateDir = templateDirAttr->getString(); if (!store->isInStore(templateDir)) - evalState->error( + evalState->errors.make( "'%s' was not found in the Nix store\n" "If you've set '%s' to a string, try using a path instead.", templateDir, templateDirAttr->getAttrPathStr()).debugThrow(); @@ -1374,7 +1374,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON { auto aType = visitor.maybeGetAttr("type"); if (!aType || aType->getString() != "app") - state->error("not an app definition").debugThrow(); + state->errors.make("not an app definition").debugThrow(); if (json) { j.emplace("type", "app"); } else { diff --git a/tests/unit/libexpr/error_traces.cc b/tests/unit/libexpr/error_traces.cc index 7017172b4..80f2578f5 100644 --- a/tests/unit/libexpr/error_traces.cc +++ b/tests/unit/libexpr/error_traces.cc @@ -12,19 +12,19 @@ namespace nix { TEST_F(ErrorTraceTest, TraceBuilder) { ASSERT_THROW( - state.error("puppy").debugThrow(), + state.errors.make("puppy").debugThrow(), EvalError ); ASSERT_THROW( - state.error("puppy").withTrace(noPos, "doggy").debugThrow(), + state.errors.make("puppy").withTrace(noPos, "doggy").debugThrow(), EvalError ); ASSERT_THROW( try { try { - state.error("puppy").withTrace(noPos, "doggy").debugThrow(); + state.errors.make("puppy").withTrace(noPos, "doggy").debugThrow(); } catch (Error & e) { e.addTrace(state.positions[noPos], "beans"); throw; @@ -47,10 +47,10 @@ namespace nix { TEST_F(ErrorTraceTest, NestedThrows) { try { - state.error("puppy").withTrace(noPos, "doggy").debugThrow(); + state.errors.make("puppy").withTrace(noPos, "doggy").debugThrow(); } catch (BaseError & e) { try { - state.error("beans").debugThrow(); + state.errors.make("beans").debugThrow(); } catch (Error & e2) { e.addTrace(state.positions[noPos], "beans2"); //e2.addTrace(state.positions[noPos], "Something", "");