diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 4b160a100ce..8db57c63b78 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -244,7 +244,7 @@ void NixRepl::mainLoop() try { createDirs(dirOf(historyFile)); } catch (SysError & e) { - logWarning(e.info()); + logExWarning(e); } #ifndef READLINE el_hist_size = 1000; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0b424367031..209c7e94e9f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -878,7 +878,7 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & .pos = error->info().errPos ? error->info().errPos : static_cast>(positions[expr.getPos()]), .expr = expr, .env = env, - .hint = error->info().msg, + .hint = error->message, .isError = true }) : nullptr; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index bb3ac2b227e..05ebf4be96e 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -94,15 +94,18 @@ class ErrorBuilder private: EvalState & state; ErrorInfo info; + hintformat message; - ErrorBuilder(EvalState & s, ErrorInfo && i): state(s), info(i) { } + ErrorBuilder(EvalState & s, hintformat && message) + : state(s), message(message) + { } public: template [[nodiscard, gnu::noinline]] static ErrorBuilder * create(EvalState & s, const Args & ... args) { - return new ErrorBuilder(s, ErrorInfo { .msg = hintfmt(args...) }); + return new ErrorBuilder(s, hintfmt(args...)); } [[nodiscard, gnu::noinline]] @@ -756,7 +759,7 @@ template void ErrorBuilder::debugThrow() { // NOTE: We always use the -LastTrace version as we push the new trace in withFrame() - state.debugThrowLastTrace(ErrorType(info)); + state.debugThrowLastTrace(ErrorType { info, message }); } } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 4d981712a9d..86d8b665133 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -33,7 +33,7 @@ namespace nix { Expr * result; SourcePath basePath; PosTable::Origin origin; - std::optional error; + std::optional error; }; struct ParserFormals { @@ -292,10 +292,10 @@ static inline PosIdx makeCurPos(const YYLTYPE & loc, ParseData * data) void yyerror(YYLTYPE * loc, yyscan_t scanner, ParseData * data, const char * error) { - data->error = { + data->error = ParseError {{ .msg = hintfmt(error), - .errPos = data->state.positions[makeCurPos(*loc, data)] - }; + .errPos = data->state.positions[makeCurPos(*loc, data)], + }}; } @@ -667,7 +667,7 @@ Expr * EvalState::parse( int res = yyparse(scanner, &data); yylex_destroy(scanner); - if (res) throw ParseError(data.error.value()); + if (res) throw data.error.value(); data.result->bindVars(*this, staticEnv); @@ -796,9 +796,9 @@ std::pair EvalState::resolveSearchPathElem(const SearchPathEl store, EvalSettings::resolvePseudoUrl(elem.second), "source", false).first.storePath; res = { true, store->toRealPath(storePath) }; } catch (FileTransferError & e) { - logWarning({ + logExWarning(Error{{ .msg = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second) - }); + }}); res = { false, "" }; } } @@ -816,9 +816,9 @@ std::pair EvalState::resolveSearchPathElem(const SearchPathEl if (pathExists(path)) res = { true, path }; else { - logWarning({ + logExWarning(Error{{ .msg = hintfmt("Nix search path entry '%1%' does not exist, ignoring", elem.second) - }); + }}); res = { false, "" }; } } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0be39fa7d7c..021d73ca08f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -744,7 +744,7 @@ static RegisterPrimOp primop_break({ .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) { if (state.debugRepl && !state.debugTraces.empty()) { - auto error = Error(ErrorInfo { + auto error = Error({ .level = lvlInfo, .msg = hintfmt("breakpoint reached"), .errPos = state.positions[pos], @@ -755,7 +755,7 @@ static RegisterPrimOp primop_break({ if (state.debugQuit) { // If the user elects to quit the repl, throw an exception. - throw Error(ErrorInfo{ + throw Error({ .level = lvlInfo, .msg = hintfmt("quit the debugger"), .errPos = nullptr, diff --git a/src/libexpr/tests/error_traces.cc b/src/libexpr/tests/error_traces.cc index 24e95ac39f7..1e1acaa8f53 100644 --- a/src/libexpr/tests/error_traces.cc +++ b/src/libexpr/tests/error_traces.cc @@ -30,7 +30,7 @@ namespace nix { throw; } } catch (BaseError & e) { - ASSERT_EQ(PrintToString(e.info().msg), + ASSERT_EQ(PrintToString(e.message), PrintToString(hintfmt("Not much"))); auto trace = e.info().traces.rbegin(); ASSERT_EQ(e.info().traces.size(), 2); @@ -61,7 +61,7 @@ namespace nix { } } -#define ASSERT_TRACE1(args, type, message) \ +#define ASSERT_TRACE1(args, type, _message) \ ASSERT_THROW( \ std::string expr(args); \ std::string name = expr.substr(0, expr.find(" ")); \ @@ -69,8 +69,8 @@ namespace nix { Value v = eval("builtins." args); \ state.forceValueDeep(v); \ } catch (BaseError & e) { \ - ASSERT_EQ(PrintToString(e.info().msg), \ - PrintToString(message)); \ + ASSERT_EQ(PrintToString(e.message), \ + PrintToString(_message)); \ ASSERT_EQ(e.info().traces.size(), 1) << "while testing " args << std::endl << e.what(); \ auto trace = e.info().traces.rbegin(); \ ASSERT_EQ(PrintToString(trace->hint), \ @@ -80,7 +80,7 @@ namespace nix { , type \ ) -#define ASSERT_TRACE2(args, type, message, context) \ +#define ASSERT_TRACE2(args, type, _message, context) \ ASSERT_THROW( \ std::string expr(args); \ std::string name = expr.substr(0, expr.find(" ")); \ @@ -88,8 +88,8 @@ namespace nix { Value v = eval("builtins." args); \ state.forceValueDeep(v); \ } catch (BaseError & e) { \ - ASSERT_EQ(PrintToString(e.info().msg), \ - PrintToString(message)); \ + ASSERT_EQ(PrintToString(e.message), \ + PrintToString(_message)); \ ASSERT_EQ(e.info().traces.size(), 2) << "while testing " args << std::endl << e.what(); \ auto trace = e.info().traces.rbegin(); \ ASSERT_EQ(PrintToString(trace->hint), \ diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc index ce3b5d11fae..253851e53e7 100644 --- a/src/libexpr/tests/primops.cc +++ b/src/libexpr/tests/primops.cc @@ -19,8 +19,8 @@ namespace nix { oss << s << std::endl; } - void logEI(const ErrorInfo & ei) override { - showErrorInfo(oss, ei, loggerSettings.showTrace.get()); + void logEI(const ErrorInfo & ei, hintformat msg) override { + showErrorInfo(oss, ei, msg, loggerSettings.showTrace.get()); } }; diff --git a/src/libexpr/value/context.hh b/src/libexpr/value/context.hh index 287ae08a942..1a8dde02d03 100644 --- a/src/libexpr/value/context.hh +++ b/src/libexpr/value/context.hh @@ -22,7 +22,7 @@ public: { raw = raw_; auto hf = hintfmt(args...); - err.msg = hintfmt("Bad String Context element: %1%: %2%", normaltxt(hf.str()), raw); + message = hintfmt("Bad String Context element: %1%: %2%", normaltxt(hf.str()), raw); } }; diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index 6600ec17728..4e9266e263a 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -145,12 +145,12 @@ class ProgressBar : public Logger log(*state, lvl, s); } - void logEI(const ErrorInfo & ei) override + void logEI(const ErrorInfo & ei, hintformat msg) override { auto state(state_.lock()); std::stringstream oss; - showErrorInfo(oss, ei, loggerSettings.showTrace.get()); + showErrorInfo(oss, ei, msg, loggerSettings.showTrace.get()); log(*state, ei.level, oss.str()); } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 56f47a4ac81..f408e5b3ada 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -334,11 +334,11 @@ int handleExceptions(const std::string & programName, std::function fun) } catch (Exit & e) { return e.status; } catch (UsageError & e) { - logError(e.info()); + logExError(e); printError("Try '%1% --help' for more information.", programName); return 1; } catch (BaseError & e) { - logError(e.info()); + logExError(e); return e.status; } catch (std::bad_alloc & e) { printError(error + "out of memory"); diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index fcd763a9d3e..dbae66eeb74 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -357,7 +357,7 @@ void BinaryCacheStore::narFromPath(const StorePath & storePath, Sink & sink) try { getFile(info->url, *decompressor); } catch (NoSuchBinaryCacheFile & e) { - throw SubstituteGone(std::move(e.info())); + throw SubstituteGone(std::move(e.info()), e.message); } decompressor->finish(); diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 5b1c923cd5a..ce8863516ea 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -1439,7 +1439,7 @@ void DerivationGoal::done( { buildResult.status = status; if (ex) - buildResult.errorMsg = fmt("%s", normaltxt(ex->info().msg)); + buildResult.errorMsg = fmt("%s", normaltxt(ex->message)); if (buildResult.status == BuildResult::TimedOut) worker.timedOut = true; if (buildResult.status == BuildResult::PermanentFailure) diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc index 74eae06928a..a7a40a7b5fc 100644 --- a/src/libstore/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -20,7 +20,7 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod for (auto & i : goals) { if (i->ex) { if (ex) - logError(i->ex->info()); + logExError(*i->ex); else ex = std::move(i->ex); } @@ -34,7 +34,7 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod ex->status = worker.exitStatus(); throw std::move(*ex); } else if (!failed.empty()) { - if (ex) logError(ex->info()); + if (ex) logExError(*ex); throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed)); } } diff --git a/src/libstore/build/goal.cc b/src/libstore/build/goal.cc index ca7097a68df..3a2ecdcc5a6 100644 --- a/src/libstore/build/goal.cc +++ b/src/libstore/build/goal.cc @@ -85,7 +85,7 @@ void Goal::amDone(ExitCode result, std::optional ex) if (ex) { if (!waiters.empty()) - logError(ex->info()); + logExError(*ex); else this->ex = std::move(*ex); } diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index e6db298d6fe..35f025e0993 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -85,10 +85,10 @@ void handleDiffHook( if (diffRes.second != "") printError(chomp(diffRes.second)); } catch (Error & error) { - ErrorInfo ei = error.info(); + Error e = error; // FIXME: wrap errors. - ei.msg = hintfmt("diff hook execution failed: %s", ei.msg.str()); - logError(ei); + e.message = hintfmt("diff hook execution failed: %s", e.message.str()); + logExError(e); } } } diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc index 190fb455a91..7982bfa4b09 100644 --- a/src/libstore/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -119,7 +119,7 @@ void PathSubstitutionGoal::tryNext() throw; } catch (Error & e) { if (settings.tryFallback) { - logError(e.info()); + logExError(e); tryNext(); return; } diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 5083497a97e..0b298b06aad 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -76,12 +76,12 @@ struct TunnelLogger : public Logger enqueueMsg(buf.s); } - void logEI(const ErrorInfo & ei) override + void logEI(const ErrorInfo & ei, hintformat msg) override { if (ei.level > verbosity) return; std::stringstream oss; - showErrorInfo(oss, ei, false); + showErrorInfo(oss, ei, msg, false); StringSink buf; buf << STDERR_NEXT << oss.str(); diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 2346accbe94..e4cedd48e85 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -870,9 +870,9 @@ FileTransferError::FileTransferError(FileTransfer::Error error, std::optionalsize() < 1024 || response->find("") != std::string::npos)) - err.msg = hintfmt("%1%\n\nresponse body:\n\n%2%", normaltxt(hf.str()), chomp(*response)); + message = hintfmt("%1%\n\nresponse body:\n\n%2%", normaltxt(hf.str()), chomp(*response)); else - err.msg = hf; + message = hf; } } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 7fb312c3770..887e1509752 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1581,7 +1581,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) /* It's possible that the path got GC'ed, so ignore errors on invalid paths. */ if (isValidPath(i)) - logError(e.info()); + logExError(e); else warn(e.msg()); errors = true; @@ -1629,7 +1629,7 @@ void LocalStore::verifyPath(const Path & pathS, const StringSet & store, try { repairPath(path); } catch (Error & e) { - logWarning(e.info()); + logExWarning(e); errors = true; } else errors = true; diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 0ed17a6ce50..f6bb74a6734 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -511,7 +511,7 @@ void RemoteStore::queryPathInfoUncached(const StorePath & path, } catch (Error & e) { // Ugly backwards compatibility hack. if (e.msg().find("is not valid") != std::string::npos) - throw InvalidPath(std::move(e.info())); + throw InvalidPath(std::move(e.info()), std::move(e.message)); throw; } if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 17) { diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc index df334c23c1a..cb539ca9d07 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -12,7 +12,7 @@ SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int ex : Error(""), path(path), errMsg(errMsg), errNo(errNo), extendedErrNo(extendedErrNo), offset(offset) { auto offsetStr = (offset == -1) ? "" : "at offset " + std::to_string(offset) + ": "; - err.msg = hintfmt("%s: %s%s, %s (in '%s')", + message = hintfmt("%s: %s%s, %s (in '%s')", normaltxt(hf.str()), offsetStr, sqlite3_errstr(extendedErrNo), @@ -31,7 +31,7 @@ SQLiteError::SQLiteError(const char *path, const char *errMsg, int errNo, int ex if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) { auto exp = SQLiteBusy(path, errMsg, err, exterr, offset, std::move(hf)); - exp.err.msg = hintfmt( + exp.message = hintfmt( err == SQLITE_PROTOCOL ? "SQLite database '%s' is busy (SQLITE_PROTOCOL)" : "SQLite database '%s' is busy", @@ -244,9 +244,7 @@ void handleSQLiteBusy(const SQLiteBusy & e, time_t & nextWarning) time_t now = time(0); if (now > nextWarning) { nextWarning = now + 10; - logWarning({ - .msg = hintfmt(e.what()) - }); + logExWarning(e); } /* Sleep for a while since retrying the transaction right away diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 5bee1af9fd1..238b663c817 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -569,7 +569,7 @@ void Store::querySubstitutablePathInfos(const StorePathCAMap & paths, Substituta } catch (SubstituterDisabled &) { } catch (Error & e) { if (settings.tryFallback) - logError(e.info()); + logExError(e); else throw; } @@ -794,7 +794,7 @@ void Store::substitutePaths(const StorePathSet & paths) for (auto & p : willSubstitute) subs.push_back(DerivedPath::Opaque{p}); buildPaths(subs); } catch (Error & e) { - logWarning(e.info()); + logExWarning(e); } } @@ -1504,7 +1504,7 @@ std::list> getDefaultSubstituters() try { stores.push_back(openStore(uri)); } catch (Error & e) { - logWarning(e.info()); + logExWarning(e); } }; diff --git a/src/libutil/error.cc b/src/libutil/error.cc index c9d61942aed..79685142c4b 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -9,25 +9,30 @@ namespace nix { const std::string nativeSystem = SYSTEM; -void BaseError::addTrace(std::shared_ptr && e, hintformat hint, bool frame) +void Base0Error::addTrace(std::shared_ptr && e, hintformat hint, bool frame) { err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .frame = frame }); } // c++ std::exception descendants must have a 'const char* what()' function. // This stringifies the error and caches it for use by what(), or similarly by msg(). -const std::string & BaseError::calcWhat() const +const std::string & Base0Error::calcWhat() const { if (what_.has_value()) return *what_; else { - std::ostringstream oss; - showErrorInfo(oss, err, loggerSettings.showTrace); - what_ = oss.str(); + what_ = calcWhatUncached(); return *what_; } } +std::string BaseError::calcWhatUncached() const +{ + std::ostringstream oss; + showErrorInfo(oss, err, message, loggerSettings.showTrace); + return oss.str(); +} + std::optional ErrorInfo::programName = std::nullopt; std::ostream & operator <<(std::ostream & os, const hintformat & hf) @@ -150,7 +155,11 @@ static std::string indent(std::string_view indentFirst, std::string_view indentR return res; } -std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace) +std::ostream & showErrorInfo( + std::ostream & out, + const ErrorInfo & einfo, + std::function msg, + bool showTrace) { std::string prefix; switch (einfo.level) { @@ -331,7 +340,9 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s oss << "\n" << prefix; } - oss << einfo.msg << "\n"; + msg(oss); + + oss << "\n"; if (einfo.errPos) { oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *einfo.errPos << ANSI_NORMAL << ":"; @@ -355,4 +366,19 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s return out; } + +std::ostream & showErrorInfo( + std::ostream & out, + const ErrorInfo & einfo, + hintformat msg, + bool showTrace) +{ + return showErrorInfo( + out, + einfo, + [&](std::ostringstream & oss) { + oss << msg; + }, + showTrace); +} } diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 6a09230812e..69e06b083c3 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -98,7 +98,6 @@ struct Trace { struct ErrorInfo { Verbosity level; - hintformat msg; std::shared_ptr errPos; std::list traces; @@ -107,55 +106,47 @@ struct ErrorInfo { static std::optional programName; }; -std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace); - -/** - * BaseError should generally not be caught, as it has Interrupted as - * a subclass. Catch Error instead. - */ -class BaseError : public std::exception +std::ostream & showErrorInfo( + std::ostream & out, + const ErrorInfo & einfo, + std::function msg, + bool showTrace); +/* Convenience for the common case. */ +std::ostream & showErrorInfo( + std::ostream & out, + const ErrorInfo & einfo, + hintformat msg, + bool showTrace); + +class Base0Error : public std::exception { protected: mutable ErrorInfo err; mutable std::optional what_; const std::string & calcWhat() const; + virtual std::string calcWhatUncached() const = 0; -public: - unsigned int status = 1; // exit status - - BaseError(const BaseError &) = default; + Base0Error(const Base0Error &) = default; - template - BaseError(unsigned int status, const Args & ... args) - : err { .level = lvlError, .msg = hintfmt(args...) } + Base0Error(unsigned int status, ErrorInfo && e) + : err(std::move(e)) , status(status) { } - template - explicit BaseError(const std::string & fs, const Args & ... args) - : err { .level = lvlError, .msg = hintfmt(fs, args...) } - { } - - template - BaseError(const Suggestions & sug, const Args & ... args) - : err { .level = lvlError, .msg = hintfmt(args...), .suggestions = sug } - { } - - BaseError(hintformat hint) - : err { .level = lvlError, .msg = hint } - { } - - BaseError(ErrorInfo && e) + Base0Error(ErrorInfo && e) : err(std::move(e)) { } - BaseError(const ErrorInfo & e) + Base0Error(const ErrorInfo & e) : err(e) { } +public: + unsigned int status = 1; // exit status + #ifdef EXCEPTION_NEEDS_THROW_SPEC - ~BaseError() throw () { }; + ~Base0Error() throw () { }; const char * what() const throw () { return calcWhat().c_str(); } #else const char * what() const noexcept override { return calcWhat().c_str(); } @@ -182,6 +173,83 @@ public: const ErrorInfo & info() { return err; }; }; + +/** + * The old version of `ErrorInfo`, with a message. + * + * This is deprecated. + * + * This just exists for the sake of existing constructions of + * `BaseError` and derived types. Once those are all converted to + * something else (e.g. structured) we should get rid of this. + */ +struct ErrorInfoCompat { + Verbosity level; + hintformat msg; + std::shared_ptr errPos; + std::list traces; + + Suggestions suggestions; + + static std::optional programName; +}; + + +/** + * Base0Error should generally not be caught, as it has `Interrupted` as + * a subclass. Catch `Error` instead. + */ +struct BaseError : Base0Error +{ + hintformat message; + + /** + * Deprecated. + */ + BaseError(ErrorInfoCompat && e) + : Base0Error(ErrorInfo { + .level = e.level, + .errPos = e.errPos, + .traces = e.traces, + .suggestions = e.suggestions, + }) + , message(e.msg) + { } + + BaseError(ErrorInfo && e, hintformat && message) + : Base0Error(e), message(message) + { } + + BaseError(const ErrorInfo & e, const hintformat & message) + : Base0Error(e), message(message) + { } + + template + BaseError(unsigned int status, const Args & ... args) + : Base0Error(status, ErrorInfo { .level = lvlError }) + , message(hintfmt(args...)) + { } + + template + explicit BaseError(const std::string & fs, const Args & ... args) + : Base0Error(ErrorInfo { .level = lvlError }) + , message(hintfmt(fs, args...)) + { } + + template + BaseError(const Suggestions & sug, const Args & ... args) + : Base0Error(ErrorInfo { .level = lvlError, .suggestions = sug }) + , message(hintfmt(args...)) + { } + + BaseError(hintformat hint) + : Base0Error(ErrorInfo { .level = lvlError }) + , message(hint) + { } + + std::string calcWhatUncached() const override; +}; + #define MakeError(newClass, superClass) \ class newClass : public superClass \ { \ @@ -204,7 +272,7 @@ public: { errNo = errNo_; auto hf = hintfmt(args...); - err.msg = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo)); + message = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo)); } template diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 5a2dd99afca..49b456173c6 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -76,10 +76,10 @@ class SimpleLogger : public Logger writeToStderr(prefix + filterANSIEscapes(s, !tty) + "\n"); } - void logEI(const ErrorInfo & ei) override + void logEI(const ErrorInfo & ei, hintformat msg) override { std::stringstream oss; - showErrorInfo(oss, ei, loggerSettings.showTrace.get()); + showErrorInfo(oss, ei, msg, loggerSettings.showTrace.get()); log(ei.level, oss.str()); } @@ -184,16 +184,16 @@ struct JSONLogger : Logger { write(json); } - void logEI(const ErrorInfo & ei) override + void logEI(const ErrorInfo & ei, hintformat message) override { std::ostringstream oss; - showErrorInfo(oss, ei, loggerSettings.showTrace.get()); + showErrorInfo(oss, ei, message, loggerSettings.showTrace.get()); nlohmann::json json; json["action"] = "msg"; json["level"] = ei.level; json["msg"] = oss.str(); - json["raw_msg"] = ei.msg.str(); + json["raw_msg"] = message.str(); to_json(json, ei.errPos); if (loggerSettings.showTrace.get() && !ei.traces.empty()) { diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 5aa6bee956a..f78426b245f 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -86,12 +86,12 @@ public: log(lvlInfo, s); } - virtual void logEI(const ErrorInfo & ei) = 0; + virtual void logEI(const ErrorInfo & ei, hintformat msg) = 0; - void logEI(Verbosity lvl, ErrorInfo ei) + void logEI(Verbosity lvl, ErrorInfo ei, hintformat msg) { ei.level = lvl; - logEI(ei); + logEI(ei, msg); } virtual void warn(const std::string & msg); @@ -194,15 +194,24 @@ extern Verbosity verbosity; * intervention or that need more explanation. Use the 'print' macros for more * lightweight status messages. */ -#define logErrorInfo(level, errorInfo...) \ +#define logErrorInfo(level, errorInfo, msg...) \ do { \ if ((level) <= nix::verbosity) { \ - logger->logEI((level), errorInfo); \ + logger->logEI((level), errorInfo, msg); \ } \ } while (0) -#define logError(errorInfo...) logErrorInfo(lvlError, errorInfo) -#define logWarning(errorInfo...) logErrorInfo(lvlWarn, errorInfo) +#define logError(errorInfo, msg...) logErrorInfo(lvlError, errorInfo, msg) +#define logWarning(errorInfo, msg...) logErrorInfo(lvlWarn, errorInfo, msg) + +#define logEx(level, error) \ + ({ \ + auto __error = std::move(error); \ + logErrorInfo(level, __error.info(), __error.message); \ + }) + +#define logExError(error) logEx(lvlError, error) +#define logExWarning(error) logEx(lvlWarn, error) /** * Print a string message if the current log level is at least the specified diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 3d5121a19fa..d33018b3d93 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -372,7 +372,7 @@ Sink & operator << (Sink & sink, const Error & ex) << "Error" << info.level << "Error" // removed - << info.msg.str() + << ex.message.str() << 0 // FIXME: info.errPos << info.traces.size(); for (auto & trace : info.traces) { @@ -441,10 +441,10 @@ Error readError(Source & source) assert(type == "Error"); auto level = (Verbosity) readInt(source); auto name = readString(source); // removed - auto msg = readString(source); + auto msg0 = readString(source); + auto msg = hintformat(fmt("%s", msg0)); ErrorInfo info { .level = level, - .msg = hintformat(fmt("%s", msg)), }; auto havePos = readNum(source); assert(havePos == 0); @@ -456,7 +456,7 @@ Error readError(Source & source) .hint = hintformat(fmt("%s", readString(source))) }); } - return Error(std::move(info)); + return Error(std::move(info), msg); } diff --git a/src/libutil/tests/logging.cc b/src/libutil/tests/logging.cc index 2ffdc2e9b88..49712f7a7e1 100644 --- a/src/libutil/tests/logging.cc +++ b/src/libutil/tests/logging.cc @@ -84,7 +84,7 @@ namespace nix { } catch (SysError &e) { testing::internal::CaptureStderr(); - logError(e.info()); + logExError(e); auto str = testing::internal::GetCapturedStderr(); ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\nstatting file: \x1B[33;1mBad file descriptor\x1B[0m\n"); @@ -277,7 +277,7 @@ namespace nix { loggerSettings.showTrace.assign(true); - logError(e.info()); + logExError(e); auto str = testing::internal::GetCapturedStderr(); ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- AssertionError --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nshow-traces\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nit has been \x1B[33;1mzero\x1B[0m days since our last error\n\x1B[34;1m---- show-trace ----\x1B[0m\n\x1B[34;1mtrace: \x1B[0mwhile trying to compute \x1B[33;1m42\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(1:19)\x1B[34;1m from stdin\x1B[0m\n\n 1| this is the other problem line of code\n | \x1B[31;1m^\x1B[0m\n\n\x1B[34;1mtrace: \x1B[0mwhile doing something without a \x1B[33;1mpos\x1B[0m\n\x1B[34;1mtrace: \x1B[0mmissing \x1B[33;1mnix file\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(100:1)\x1B[34;1m in file: \x1B[0minvalid filename\n"); @@ -303,7 +303,7 @@ namespace nix { loggerSettings.showTrace.assign(false); - logError(e.info()); + logExError(e); auto str = testing::internal::GetCapturedStderr(); ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- AssertionError --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nhide traces\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nit has been \x1B[33;1mzero\x1B[0m days since our last error\n"); diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 21d1c8dcd4f..580dc3d02fa 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -965,7 +965,7 @@ int Pid::kill() #if __FreeBSD__ || __APPLE__ if (errno != EPERM || ::kill(pid, 0) != 0) #endif - logError(SysError("killing process %d", pid).info()); + logExError(SysError("killing process %d", pid)); } return wait(); diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 6510df8f041..a947d76ddf5 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -400,7 +400,7 @@ static void main_nix_build(int argc, char * * argv) shellDrv = bashDrv; } catch (Error & e) { - logError(e.info()); + logExError(e); notice("will use bash from your environment"); shell = "bash"; } diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc index c1a91c63d8c..0c18e3746b8 100644 --- a/src/nix/daemon.cc +++ b/src/nix/daemon.cc @@ -373,8 +373,8 @@ static void daemonLoop(std::optional forceTrustClientOpt) } catch (Error & error) { auto ei = error.info(); // FIXME: add to trace? - ei.msg = hintfmt("error processing connection: %1%", ei.msg.str()); - logError(ei); + auto msg = hintfmt("error processing connection: %1%", error.message.str()); + logError(ei, msg); } } } diff --git a/src/nix/verify.cc b/src/nix/verify.cc index 0b306cc1159..b9c288f0a49 100644 --- a/src/nix/verify.cc +++ b/src/nix/verify.cc @@ -146,7 +146,7 @@ struct CmdVerify : StorePathsCommand doSigs(info2->sigs); } catch (InvalidPath &) { } catch (Error & e) { - logError(e.info()); + logExError(e); } } @@ -165,7 +165,7 @@ struct CmdVerify : StorePathsCommand done++; } catch (Error & e) { - logError(e.info()); + logExError(e); failed++; }