From 173dcb0af9249487c2d9ad5de7218fcf203873bd Mon Sep 17 00:00:00 2001 From: Florian Friesdorf Date: Tue, 22 Nov 2022 12:46:55 +0000 Subject: Don't reverse stack trace when showing When debugging nix expressions the outermost trace tends to be more useful than the innermost. It is therefore printed last to save developers from scrolling. --- src/libutil/error.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libutil/error.cc') diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 9172f67a6..9cac6ac91 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -287,7 +287,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s // traces if (showTrace && !einfo.traces.empty()) { - for (auto iter = einfo.traces.rbegin(); iter != einfo.traces.rend(); ++iter) { + for (auto iter = einfo.traces.begin(); iter != einfo.traces.end(); ++iter) { oss << "\n" << "… " << iter->hint.str() << "\n"; if (iter->pos.has_value() && (*iter->pos)) { -- cgit v1.2.3 From d269976be6def2928e6a315ab2b85b947f4308f2 Mon Sep 17 00:00:00 2001 From: Florian Friesdorf Date: Tue, 22 Nov 2022 16:45:58 +0000 Subject: Show stack trace above error message Save developers from scrolling by displaying the error message last, below the stack trace. --- src/libutil/error.cc | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'src/libutil/error.cc') diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 9cac6ac91..449baaad1 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -262,6 +262,28 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s prefix += ":" ANSI_NORMAL " "; std::ostringstream oss; + + // traces + if (showTrace && !einfo.traces.empty()) { + for (auto iter = einfo.traces.begin(); iter != einfo.traces.end(); ++iter) { + oss << "\n" << "… " << iter->hint.str() << "\n"; + + if (iter->pos.has_value() && (*iter->pos)) { + auto pos = iter->pos.value(); + oss << "\n"; + printAtPos(pos, oss); + + auto loc = getCodeLines(pos); + if (loc.has_value()) { + oss << "\n"; + printCodeLines(oss, "", pos, *loc); + oss << "\n"; + } + } + } + oss << "\n" << prefix; + } + oss << einfo.msg << "\n"; if (einfo.errPos.has_value() && *einfo.errPos) { @@ -285,26 +307,6 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s "?" << std::endl; } - // traces - if (showTrace && !einfo.traces.empty()) { - for (auto iter = einfo.traces.begin(); iter != einfo.traces.end(); ++iter) { - oss << "\n" << "… " << iter->hint.str() << "\n"; - - if (iter->pos.has_value() && (*iter->pos)) { - auto pos = iter->pos.value(); - oss << "\n"; - printAtPos(pos, oss); - - auto loc = getCodeLines(pos); - if (loc.has_value()) { - oss << "\n"; - printCodeLines(oss, "", pos, *loc); - oss << "\n"; - } - } - } - } - out << indent(prefix, std::string(filterANSIEscapes(prefix, true).size(), ' '), chomp(oss.str())); return out; -- cgit v1.2.3 From 8618c6cc75e19ed658649bd806b218de954ea3bc Mon Sep 17 00:00:00 2001 From: Florian Friesdorf Date: Fri, 9 Dec 2022 17:36:25 +0000 Subject: Simplify loop, feedback from @tfc and @Ericson2314 --- src/libutil/error.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/libutil/error.cc') diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 449baaad1..3bb3efb0e 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -265,11 +265,11 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s // traces if (showTrace && !einfo.traces.empty()) { - for (auto iter = einfo.traces.begin(); iter != einfo.traces.end(); ++iter) { - oss << "\n" << "… " << iter->hint.str() << "\n"; + for (const auto & trace : einfo.traces) { + oss << "\n" << "… " << trace.hint.str() << "\n"; - if (iter->pos.has_value() && (*iter->pos)) { - auto pos = iter->pos.value(); + if (trace.pos.has_value() && (*trace.pos)) { + auto pos = trace.pos.value(); oss << "\n"; printAtPos(pos, oss); -- cgit v1.2.3 From b3fdab28a216683365f7f04bfa9bbc5cd122d753 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 13 Dec 2022 00:48:04 +0100 Subject: Introduce AbstractPos This makes the position object used in exceptions abstract, with a method getSource() to get the source code of the file in which the error originated. This is needed for lazy trees because source files don't necessarily exist in the filesystem, and we don't want to make libutil depend on the InputAccessor type in libfetcher. --- src/libutil/error.cc | 144 ++++++++++++++------------------------------------- 1 file changed, 38 insertions(+), 106 deletions(-) (limited to 'src/libutil/error.cc') diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 3bb3efb0e..1a1aecea5 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -9,9 +9,9 @@ namespace nix { const std::string nativeSystem = SYSTEM; -void BaseError::addTrace(std::optional e, hintformat hint) +void BaseError::addTrace(std::shared_ptr && e, hintformat hint) { - err.traces.push_front(Trace { .pos = e, .hint = hint }); + err.traces.push_front(Trace { .pos = std::move(e), .hint = hint }); } // c++ std::exception descendants must have a 'const char* what()' function. @@ -30,91 +30,46 @@ const std::string & BaseError::calcWhat() const std::optional ErrorInfo::programName = std::nullopt; -std::ostream & operator<<(std::ostream & os, const hintformat & hf) +std::ostream & operator <<(std::ostream & os, const hintformat & hf) { return os << hf.str(); } -std::string showErrPos(const ErrPos & errPos) +std::ostream & operator <<(std::ostream & str, const AbstractPos & pos) { - if (errPos.line > 0) { - if (errPos.column > 0) { - return fmt("%d:%d", errPos.line, errPos.column); - } else { - return fmt("%d", errPos.line); - } - } - else { - return ""; - } + pos.print(str); + str << ":" << pos.line; + if (pos.column > 0) + str << ":" << pos.column; + return str; } -std::optional getCodeLines(const ErrPos & errPos) +std::optional AbstractPos::getCodeLines() const { - if (errPos.line <= 0) + if (line == 0) return std::nullopt; - if (errPos.origin == foFile) { - LinesOfCode loc; - try { - // FIXME: when running as the daemon, make sure we don't - // open a file to which the client doesn't have access. - AutoCloseFD fd = open(errPos.file.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) return {}; - - // count the newlines. - int count = 0; - std::string line; - int pl = errPos.line - 1; - do - { - line = readLine(fd.get()); - ++count; - if (count < pl) - ; - else if (count == pl) - loc.prevLineOfCode = line; - else if (count == pl + 1) - loc.errLineOfCode = line; - else if (count == pl + 2) { - loc.nextLineOfCode = line; - break; - } - } while (true); - return loc; - } - catch (EndOfFile & eof) { - if (loc.errLineOfCode.has_value()) - return loc; - else - return std::nullopt; - } - catch (std::exception & e) { - return std::nullopt; - } - } else { - std::istringstream iss(errPos.file); + if (auto source = getSource()) { + + std::istringstream iss(*source); // count the newlines. int count = 0; - std::string line; - int pl = errPos.line - 1; + std::string curLine; + int pl = line - 1; LinesOfCode loc; - do - { - std::getline(iss, line); + do { + std::getline(iss, curLine); ++count; if (count < pl) - { ; - } else if (count == pl) { - loc.prevLineOfCode = line; + loc.prevLineOfCode = curLine; } else if (count == pl + 1) { - loc.errLineOfCode = line; + loc.errLineOfCode = curLine; } else if (count == pl + 2) { - loc.nextLineOfCode = line; + loc.nextLineOfCode = curLine; break; } @@ -124,12 +79,14 @@ std::optional getCodeLines(const ErrPos & errPos) return loc; } + + return std::nullopt; } // print lines of code to the ostream, indicating the error column. void printCodeLines(std::ostream & out, const std::string & prefix, - const ErrPos & errPos, + const AbstractPos & errPos, const LinesOfCode & loc) { // previous line of code. @@ -176,28 +133,6 @@ void printCodeLines(std::ostream & out, } } -void printAtPos(const ErrPos & pos, std::ostream & out) -{ - if (pos) { - switch (pos.origin) { - case foFile: { - out << fmt(ANSI_BLUE "at " ANSI_WARNING "%s:%s" ANSI_NORMAL ":", pos.file, showErrPos(pos)); - break; - } - case foString: { - out << fmt(ANSI_BLUE "at " ANSI_WARNING "«string»:%s" ANSI_NORMAL ":", showErrPos(pos)); - break; - } - case foStdin: { - out << fmt(ANSI_BLUE "at " ANSI_WARNING "«stdin»:%s" ANSI_NORMAL ":", showErrPos(pos)); - break; - } - default: - throw Error("invalid FileOrigin in errPos"); - } - } -} - static std::string indent(std::string_view indentFirst, std::string_view indentRest, std::string_view s) { std::string res; @@ -263,22 +198,22 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s std::ostringstream oss; + auto noSource = ANSI_ITALIC " (source not available)" ANSI_NORMAL "\n"; + // traces if (showTrace && !einfo.traces.empty()) { for (const auto & trace : einfo.traces) { oss << "\n" << "… " << trace.hint.str() << "\n"; - if (trace.pos.has_value() && (*trace.pos)) { - auto pos = trace.pos.value(); - oss << "\n"; - printAtPos(pos, oss); + if (trace.pos) { + oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *trace.pos << ANSI_NORMAL << ":"; - auto loc = getCodeLines(pos); - if (loc.has_value()) { + if (auto loc = trace.pos->getCodeLines()) { oss << "\n"; - printCodeLines(oss, "", pos, *loc); + printCodeLines(oss, "", *trace.pos, *loc); oss << "\n"; - } + } else + oss << noSource; } } oss << "\n" << prefix; @@ -286,22 +221,19 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s oss << einfo.msg << "\n"; - if (einfo.errPos.has_value() && *einfo.errPos) { - oss << "\n"; - printAtPos(*einfo.errPos, oss); - - auto loc = getCodeLines(*einfo.errPos); + if (einfo.errPos) { + oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *einfo.errPos << ANSI_NORMAL << ":"; - // lines of code. - if (loc.has_value()) { + if (auto loc = einfo.errPos->getCodeLines()) { oss << "\n"; printCodeLines(oss, "", *einfo.errPos, *loc); oss << "\n"; - } + } else + oss << noSource; } auto suggestions = einfo.suggestions.trim(); - if (! suggestions.suggestions.empty()){ + if (!suggestions.suggestions.empty()) { oss << "Did you mean " << suggestions.trim() << "?" << std::endl; -- cgit v1.2.3