This commit is contained in:
Ben Burdette 2020-06-18 15:25:26 -06:00
parent e6f93b94fc
commit 4d1a4f0217
3 changed files with 59 additions and 44 deletions

View file

@ -239,13 +239,13 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
try {
parsed = state.parseExprFromString(output, pos.file);
} catch (Error & e) {
e.addPrefix(fmt("While parsing the output from '%1%', at %2%\n", program, pos));
e.addTrace(pos, hintfmt("While parsing the output from '%1%'", program));
throw;
}
try {
state.eval(parsed, v);
} catch (Error & e) {
e.addPrefix(fmt("While evaluating the output from '%1%', at %2%\n", program, pos));
e.addTrace(pos, hintfmt("While evaluating the output from '%1%'", program));
throw;
}
}

View file

@ -18,6 +18,14 @@ BaseError & BaseError::addPrefix(const FormatOrString & fs)
return *this;
}
// addPrefix is used for show-trace. Strings added with addPrefix
// will print ahead of the error itself.
BaseError & BaseError::addTrace(hintformat hint, ErrPos e)
{
err.traces.push_front(Trace { .pos = e, .hint = hint});
return *this;
}
// 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 string& BaseError::calcWhat() const
@ -58,7 +66,7 @@ string showErrPos(const ErrPos &errPos)
void getCodeLines(NixCode &nixCode)
{
if (nixCode.errPos.line <= 0)
if (nixCode.errPos.line <= 0)
return;
if (nixCode.errPos.origin == foFile) {
@ -72,13 +80,13 @@ void getCodeLines(NixCode &nixCode)
int count = 0;
string line;
int pl = nixCode.errPos.line - 1;
do
do
{
line = readLine(fd.get());
++count;
if (count < pl)
if (count < pl)
{
;
;
}
else if (count == pl) {
nixCode.prevLineOfCode = line;
@ -89,7 +97,7 @@ void getCodeLines(NixCode &nixCode)
break;
}
} while (true);
}
}
}
catch (EndOfFile &eof) {
;
@ -97,20 +105,20 @@ void getCodeLines(NixCode &nixCode)
catch (std::exception &e) {
printError("error reading nix file: %s\n%s", nixCode.errPos.file, e.what());
}
} else {
} else {
std::istringstream iss(nixCode.errPos.file);
// count the newlines.
int count = 0;
string line;
int pl = nixCode.errPos.line - 1;
do
do
{
std::getline(iss, line);
++count;
if (count < pl)
if (count < pl)
{
;
;
}
else if (count == pl) {
nixCode.prevLineOfCode = line;
@ -134,18 +142,18 @@ void printCodeLines(std::ostream &out, const string &prefix, const NixCode &nixC
if (nixCode.prevLineOfCode.has_value()) {
out << std::endl
<< fmt("%1% %|2$5d|| %3%",
prefix,
(nixCode.errPos.line - 1),
*nixCode.prevLineOfCode);
prefix,
(nixCode.errPos.line - 1),
*nixCode.prevLineOfCode);
}
if (nixCode.errLineOfCode.has_value()) {
// line of code containing the error.
out << std::endl
<< fmt("%1% %|2$5d|| %3%",
prefix,
(nixCode.errPos.line),
*nixCode.errLineOfCode);
prefix,
(nixCode.errPos.line),
*nixCode.errLineOfCode);
// error arrows for the column range.
if (nixCode.errPos.column > 0) {
int start = nixCode.errPos.column;
@ -158,9 +166,9 @@ void printCodeLines(std::ostream &out, const string &prefix, const NixCode &nixC
out << std::endl
<< fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL,
prefix,
spaces,
arrows);
prefix,
spaces,
arrows);
}
}
@ -168,9 +176,9 @@ void printCodeLines(std::ostream &out, const string &prefix, const NixCode &nixC
if (nixCode.nextLineOfCode.has_value()) {
out << std::endl
<< fmt("%1% %|2$5d|| %3%",
prefix,
(nixCode.errPos.line + 1),
*nixCode.nextLineOfCode);
prefix,
(nixCode.errPos.line + 1),
*nixCode.nextLineOfCode);
}
}
@ -287,7 +295,7 @@ std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo)
if (einfo.nixCode.has_value()) {
NixCode nixcode = *einfo.nixCode;
getCodeLines(nixcode);
// lines of code.
if (nixcode.errLineOfCode.has_value()) {
if (nl)

View file

@ -25,20 +25,20 @@ namespace nix {
/*
This file defines two main structs/classes used in nix error handling.
This file defines two main structs/classes used in nix error handling.
ErrorInfo provides a standard payload of error information, with conversion to string
happening in the logger rather than at the call site.
ErrorInfo provides a standard payload of error information, with conversion to string
happening in the logger rather than at the call site.
BaseError is the ancestor of nix specific exceptions (and Interrupted), and contains
an ErrorInfo.
BaseError is the ancestor of nix specific exceptions (and Interrupted), and contains
an ErrorInfo.
ErrorInfo structs are sent to the logger as part of an exception, or directly with the
logError or logWarning macros.
ErrorInfo structs are sent to the logger as part of an exception, or directly with the
logError or logWarning macros.
See the error-demo.cc program for usage examples.
See the error-demo.cc program for usage examples.
*/
*/
typedef enum {
lvlError = 0,
@ -50,7 +50,7 @@ typedef enum {
lvlVomit
} Verbosity;
typedef enum {
typedef enum {
foFile,
foStdin,
foString
@ -93,12 +93,18 @@ struct NixCode {
std::optional<string> nextLineOfCode;
};
struct Trace {
ErrPos pos;
hintformat hint;
};
struct ErrorInfo {
Verbosity level;
string name;
string description;
std::optional<hintformat> hint;
std::optional<NixCode> nixCode;
std::list<Trace> traces;
static std::optional<string> programName;
};
@ -121,23 +127,23 @@ public:
template<typename... Args>
BaseError(unsigned int status, const Args & ... args)
: err { .level = lvlError,
.hint = hintfmt(args...)
}
: err {.level = lvlError,
.hint = hintfmt(args...)
}
, status(status)
{ }
template<typename... Args>
BaseError(const std::string & fs, const Args & ... args)
: err { .level = lvlError,
.hint = hintfmt(fs, args...)
}
: err {.level = lvlError,
.hint = hintfmt(fs, args...)
}
{ }
BaseError(hintformat hint)
: err { .level = lvlError,
.hint = hint
}
: err {.level = lvlError,
.hint = hint
}
{ }
BaseError(ErrorInfo && e)
@ -160,6 +166,7 @@ public:
const string & msg() const { return calcWhat(); }
const string & prefix() const { return prefix_; }
BaseError & addPrefix(const FormatOrString & fs);
BaseError & addTrace(ErrPos e, hintformat hint);
const ErrorInfo & info() { calcWhat(); return err; }
};
@ -181,7 +188,7 @@ public:
template<typename... Args>
SysError(const Args & ... args)
:Error("")
: Error("")
{
errNo = errno;
auto hf = hintfmt(args...);