2020-04-26 20:47:41 +00:00
|
|
|
#pragma once
|
2023-04-07 13:55:28 +00:00
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
*
|
|
|
|
* @brief 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.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
* See libutil/tests/logging.cc for usage examples.
|
|
|
|
*/
|
2020-03-22 18:25:47 +00:00
|
|
|
|
2022-03-03 09:50:35 +00:00
|
|
|
#include "suggestions.hh"
|
2020-04-26 20:47:41 +00:00
|
|
|
#include "ref.hh"
|
2020-04-03 14:48:20 +00:00
|
|
|
#include "types.hh"
|
2020-07-07 12:37:47 +00:00
|
|
|
#include "fmt.hh"
|
2020-03-27 16:55:09 +00:00
|
|
|
|
2020-06-15 23:35:07 +00:00
|
|
|
#include <cstring>
|
2020-04-26 20:47:41 +00:00
|
|
|
#include <list>
|
|
|
|
#include <memory>
|
|
|
|
#include <map>
|
|
|
|
#include <optional>
|
2024-03-04 06:35:20 +00:00
|
|
|
#include <compare>
|
2020-03-22 18:25:47 +00:00
|
|
|
|
2020-07-07 12:37:47 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2020-03-22 18:25:47 +00:00
|
|
|
|
2020-04-26 20:47:41 +00:00
|
|
|
namespace nix {
|
|
|
|
|
2020-06-04 17:53:19 +00:00
|
|
|
|
2020-04-29 03:06:08 +00:00
|
|
|
typedef enum {
|
2020-04-26 20:47:41 +00:00
|
|
|
lvlError = 0,
|
|
|
|
lvlWarn,
|
2020-11-27 10:19:36 +00:00
|
|
|
lvlNotice,
|
2020-04-26 20:47:41 +00:00
|
|
|
lvlInfo,
|
|
|
|
lvlTalkative,
|
|
|
|
lvlChatty,
|
|
|
|
lvlDebug,
|
|
|
|
lvlVomit
|
|
|
|
} Verbosity;
|
|
|
|
|
2023-03-27 01:12:25 +00:00
|
|
|
/**
|
|
|
|
* The lines of code surrounding an error.
|
|
|
|
*/
|
2020-06-24 14:33:53 +00:00
|
|
|
struct LinesOfCode {
|
2022-02-25 15:00:00 +00:00
|
|
|
std::optional<std::string> prevLineOfCode;
|
|
|
|
std::optional<std::string> errLineOfCode;
|
|
|
|
std::optional<std::string> nextLineOfCode;
|
2020-06-24 14:33:53 +00:00
|
|
|
};
|
|
|
|
|
2024-03-06 04:24:35 +00:00
|
|
|
struct Pos;
|
2022-04-29 17:27:38 +00:00
|
|
|
|
2021-09-13 17:57:25 +00:00
|
|
|
void printCodeLines(std::ostream & out,
|
2022-04-07 19:42:01 +00:00
|
|
|
const std::string & prefix,
|
2024-03-06 04:24:35 +00:00
|
|
|
const Pos & errPos,
|
2021-09-13 17:57:25 +00:00
|
|
|
const LinesOfCode & loc);
|
|
|
|
|
2020-06-18 21:25:26 +00:00
|
|
|
struct Trace {
|
2024-03-06 04:24:35 +00:00
|
|
|
std::shared_ptr<Pos> pos;
|
2024-03-08 07:10:05 +00:00
|
|
|
HintFmt hint;
|
2020-03-24 17:21:35 +00:00
|
|
|
};
|
|
|
|
|
2024-03-04 06:35:20 +00:00
|
|
|
inline bool operator<(const Trace& lhs, const Trace& rhs);
|
|
|
|
inline bool operator> (const Trace& lhs, const Trace& rhs);
|
|
|
|
inline bool operator<=(const Trace& lhs, const Trace& rhs);
|
|
|
|
inline bool operator>=(const Trace& lhs, const Trace& rhs);
|
|
|
|
|
2020-04-29 03:06:08 +00:00
|
|
|
struct ErrorInfo {
|
2020-04-26 20:47:41 +00:00
|
|
|
Verbosity level;
|
2024-03-08 07:10:05 +00:00
|
|
|
HintFmt msg;
|
libexpr: Support structured error classes
While preparing PRs like #9753, I've had to change error messages in
dozens of code paths. It would be nice if instead of
EvalError("expected 'boolean' but found '%1%'", showType(v))
we could write
TypeError(v, "boolean")
or similar. Then, changing the error message could be a mechanical
refactor with the compiler pointing out places the constructor needs to
be changed, rather than the error-prone process of grepping through the
codebase. Structured errors would also help prevent the "same" error
from having multiple slightly different messages, and could be a first
step towards error codes / an error index.
This PR reworks the exception infrastructure in `libexpr` to
support exception types with different constructor signatures than
`BaseError`. Actually refactoring the exceptions to use structured data
will come in a future PR (this one is big enough already, as it has to
touch every exception in `libexpr`).
The core design is in `eval-error.hh`. Generally, errors like this:
state.error("'%s' is not a string", getAttrPathStr())
.debugThrow<TypeError>()
are transformed like this:
state.error<TypeError>("'%s' is not a string", getAttrPathStr())
.debugThrow()
The type annotation has moved from `ErrorBuilder::debugThrow` to
`EvalState::error`.
(cherry picked from commit c6a89c1a1659b31694c0fbcd21d78a6dd521c732)
Change-Id: Iced91ba4e00ca9e801518071fb43798936cbd05a
2024-03-08 06:09:48 +00:00
|
|
|
std::shared_ptr<Pos> pos;
|
2020-06-18 21:25:26 +00:00
|
|
|
std::list<Trace> traces;
|
2020-03-24 17:21:35 +00:00
|
|
|
|
libexpr: Support structured error classes
While preparing PRs like #9753, I've had to change error messages in
dozens of code paths. It would be nice if instead of
EvalError("expected 'boolean' but found '%1%'", showType(v))
we could write
TypeError(v, "boolean")
or similar. Then, changing the error message could be a mechanical
refactor with the compiler pointing out places the constructor needs to
be changed, rather than the error-prone process of grepping through the
codebase. Structured errors would also help prevent the "same" error
from having multiple slightly different messages, and could be a first
step towards error codes / an error index.
This PR reworks the exception infrastructure in `libexpr` to
support exception types with different constructor signatures than
`BaseError`. Actually refactoring the exceptions to use structured data
will come in a future PR (this one is big enough already, as it has to
touch every exception in `libexpr`).
The core design is in `eval-error.hh`. Generally, errors like this:
state.error("'%s' is not a string", getAttrPathStr())
.debugThrow<TypeError>()
are transformed like this:
state.error<TypeError>("'%s' is not a string", getAttrPathStr())
.debugThrow()
The type annotation has moved from `ErrorBuilder::debugThrow` to
`EvalState::error`.
(cherry picked from commit c6a89c1a1659b31694c0fbcd21d78a6dd521c732)
Change-Id: Iced91ba4e00ca9e801518071fb43798936cbd05a
2024-03-08 06:09:48 +00:00
|
|
|
/**
|
|
|
|
* Exit status.
|
|
|
|
*/
|
|
|
|
unsigned int status = 1;
|
|
|
|
|
2022-03-03 09:50:35 +00:00
|
|
|
Suggestions suggestions;
|
|
|
|
|
2022-02-25 15:00:00 +00:00
|
|
|
static std::optional<std::string> programName;
|
2020-04-07 01:43:22 +00:00
|
|
|
};
|
2020-03-24 17:21:35 +00:00
|
|
|
|
2022-02-25 15:00:00 +00:00
|
|
|
std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool showTrace);
|
2020-03-24 17:21:35 +00:00
|
|
|
|
2023-03-27 01:12:25 +00:00
|
|
|
/**
|
|
|
|
* BaseError should generally not be caught, as it has Interrupted as
|
|
|
|
* a subclass. Catch Error instead.
|
|
|
|
*/
|
2020-04-26 20:47:41 +00:00
|
|
|
class BaseError : public std::exception
|
2020-04-07 01:43:22 +00:00
|
|
|
{
|
2020-04-26 20:47:41 +00:00
|
|
|
protected:
|
2020-05-07 22:43:36 +00:00
|
|
|
mutable ErrorInfo err;
|
2020-04-30 00:57:05 +00:00
|
|
|
|
libexpr: Support structured error classes
While preparing PRs like #9753, I've had to change error messages in
dozens of code paths. It would be nice if instead of
EvalError("expected 'boolean' but found '%1%'", showType(v))
we could write
TypeError(v, "boolean")
or similar. Then, changing the error message could be a mechanical
refactor with the compiler pointing out places the constructor needs to
be changed, rather than the error-prone process of grepping through the
codebase. Structured errors would also help prevent the "same" error
from having multiple slightly different messages, and could be a first
step towards error codes / an error index.
This PR reworks the exception infrastructure in `libexpr` to
support exception types with different constructor signatures than
`BaseError`. Actually refactoring the exceptions to use structured data
will come in a future PR (this one is big enough already, as it has to
touch every exception in `libexpr`).
The core design is in `eval-error.hh`. Generally, errors like this:
state.error("'%s' is not a string", getAttrPathStr())
.debugThrow<TypeError>()
are transformed like this:
state.error<TypeError>("'%s' is not a string", getAttrPathStr())
.debugThrow()
The type annotation has moved from `ErrorBuilder::debugThrow` to
`EvalState::error`.
(cherry picked from commit c6a89c1a1659b31694c0fbcd21d78a6dd521c732)
Change-Id: Iced91ba4e00ca9e801518071fb43798936cbd05a
2024-03-08 06:09:48 +00:00
|
|
|
/**
|
|
|
|
* Cached formatted contents of `err.msg`.
|
|
|
|
*/
|
2022-02-25 15:00:00 +00:00
|
|
|
mutable std::optional<std::string> what_;
|
libexpr: Support structured error classes
While preparing PRs like #9753, I've had to change error messages in
dozens of code paths. It would be nice if instead of
EvalError("expected 'boolean' but found '%1%'", showType(v))
we could write
TypeError(v, "boolean")
or similar. Then, changing the error message could be a mechanical
refactor with the compiler pointing out places the constructor needs to
be changed, rather than the error-prone process of grepping through the
codebase. Structured errors would also help prevent the "same" error
from having multiple slightly different messages, and could be a first
step towards error codes / an error index.
This PR reworks the exception infrastructure in `libexpr` to
support exception types with different constructor signatures than
`BaseError`. Actually refactoring the exceptions to use structured data
will come in a future PR (this one is big enough already, as it has to
touch every exception in `libexpr`).
The core design is in `eval-error.hh`. Generally, errors like this:
state.error("'%s' is not a string", getAttrPathStr())
.debugThrow<TypeError>()
are transformed like this:
state.error<TypeError>("'%s' is not a string", getAttrPathStr())
.debugThrow()
The type annotation has moved from `ErrorBuilder::debugThrow` to
`EvalState::error`.
(cherry picked from commit c6a89c1a1659b31694c0fbcd21d78a6dd521c732)
Change-Id: Iced91ba4e00ca9e801518071fb43798936cbd05a
2024-03-08 06:09:48 +00:00
|
|
|
/**
|
|
|
|
* Format `err.msg` and set `what_` to the resulting value.
|
|
|
|
*/
|
2022-02-25 15:00:00 +00:00
|
|
|
const std::string & calcWhat() const;
|
2020-06-15 12:06:58 +00:00
|
|
|
|
2020-04-07 01:43:22 +00:00
|
|
|
public:
|
2023-01-19 12:23:04 +00:00
|
|
|
BaseError(const BaseError &) = default;
|
|
|
|
|
2020-04-26 20:47:41 +00:00
|
|
|
template<typename... Args>
|
|
|
|
BaseError(unsigned int status, const Args & ... args)
|
2024-03-08 07:10:05 +00:00
|
|
|
: err { .level = lvlError, .msg = HintFmt(args...), .status = status }
|
2020-04-30 00:57:05 +00:00
|
|
|
{ }
|
2020-04-26 20:47:41 +00:00
|
|
|
|
|
|
|
template<typename... Args>
|
2022-01-17 18:28:42 +00:00
|
|
|
explicit BaseError(const std::string & fs, const Args & ... args)
|
2024-03-08 07:10:05 +00:00
|
|
|
: err { .level = lvlError, .msg = HintFmt(fs, args...) }
|
2020-04-30 00:57:05 +00:00
|
|
|
{ }
|
2020-04-26 20:47:41 +00:00
|
|
|
|
2022-03-03 09:50:35 +00:00
|
|
|
template<typename... Args>
|
|
|
|
BaseError(const Suggestions & sug, const Args & ... args)
|
2024-03-08 07:10:05 +00:00
|
|
|
: err { .level = lvlError, .msg = HintFmt(args...), .suggestions = sug }
|
2022-03-03 09:50:35 +00:00
|
|
|
{ }
|
|
|
|
|
2024-03-08 07:10:05 +00:00
|
|
|
BaseError(HintFmt hint)
|
2021-01-20 23:27:36 +00:00
|
|
|
: err { .level = lvlError, .msg = hint }
|
2020-05-03 14:01:25 +00:00
|
|
|
{ }
|
|
|
|
|
2020-06-15 12:06:58 +00:00
|
|
|
BaseError(ErrorInfo && e)
|
|
|
|
: err(std::move(e))
|
|
|
|
{ }
|
|
|
|
|
|
|
|
BaseError(const ErrorInfo & e)
|
2020-04-26 20:47:41 +00:00
|
|
|
: err(e)
|
2020-04-30 00:57:05 +00:00
|
|
|
{ }
|
2020-04-29 03:06:08 +00:00
|
|
|
|
2020-05-07 22:43:36 +00:00
|
|
|
const char * what() const noexcept override { return calcWhat().c_str(); }
|
2022-02-25 15:00:00 +00:00
|
|
|
const std::string & msg() const { return calcWhat(); }
|
2020-10-07 14:33:19 +00:00
|
|
|
const ErrorInfo & info() const { calcWhat(); return err; }
|
2020-06-24 19:10:41 +00:00
|
|
|
|
libexpr: Support structured error classes
While preparing PRs like #9753, I've had to change error messages in
dozens of code paths. It would be nice if instead of
EvalError("expected 'boolean' but found '%1%'", showType(v))
we could write
TypeError(v, "boolean")
or similar. Then, changing the error message could be a mechanical
refactor with the compiler pointing out places the constructor needs to
be changed, rather than the error-prone process of grepping through the
codebase. Structured errors would also help prevent the "same" error
from having multiple slightly different messages, and could be a first
step towards error codes / an error index.
This PR reworks the exception infrastructure in `libexpr` to
support exception types with different constructor signatures than
`BaseError`. Actually refactoring the exceptions to use structured data
will come in a future PR (this one is big enough already, as it has to
touch every exception in `libexpr`).
The core design is in `eval-error.hh`. Generally, errors like this:
state.error("'%s' is not a string", getAttrPathStr())
.debugThrow<TypeError>()
are transformed like this:
state.error<TypeError>("'%s' is not a string", getAttrPathStr())
.debugThrow()
The type annotation has moved from `ErrorBuilder::debugThrow` to
`EvalState::error`.
(cherry picked from commit c6a89c1a1659b31694c0fbcd21d78a6dd521c732)
Change-Id: Iced91ba4e00ca9e801518071fb43798936cbd05a
2024-03-08 06:09:48 +00:00
|
|
|
void withExitStatus(unsigned int status)
|
|
|
|
{
|
|
|
|
err.status = status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void atPos(std::shared_ptr<Pos> pos) {
|
|
|
|
err.pos = pos;
|
|
|
|
}
|
|
|
|
|
2023-01-19 12:23:04 +00:00
|
|
|
void pushTrace(Trace trace)
|
|
|
|
{
|
|
|
|
err.traces.push_front(trace);
|
|
|
|
}
|
|
|
|
|
2020-06-24 19:46:25 +00:00
|
|
|
template<typename... Args>
|
2024-03-06 04:24:35 +00:00
|
|
|
void addTrace(std::shared_ptr<Pos> && e, std::string_view fs, const Args & ... args)
|
2020-06-24 19:46:25 +00:00
|
|
|
{
|
2024-03-08 07:10:05 +00:00
|
|
|
addTrace(std::move(e), HintFmt(std::string(fs), args...));
|
2020-06-24 19:46:25 +00:00
|
|
|
}
|
|
|
|
|
2024-03-08 08:47:09 +00:00
|
|
|
void addTrace(std::shared_ptr<Pos> && e, HintFmt hint);
|
2020-06-25 00:31:28 +00:00
|
|
|
|
|
|
|
bool hasTrace() const { return !err.traces.empty(); }
|
2023-01-19 12:23:04 +00:00
|
|
|
|
|
|
|
const ErrorInfo & info() { return err; };
|
2020-04-07 01:43:22 +00:00
|
|
|
};
|
|
|
|
|
2020-04-26 20:47:41 +00:00
|
|
|
#define MakeError(newClass, superClass) \
|
|
|
|
class newClass : public superClass \
|
|
|
|
{ \
|
|
|
|
public: \
|
|
|
|
using superClass::superClass; \
|
|
|
|
}
|
2020-04-08 15:07:58 +00:00
|
|
|
|
2020-04-26 20:47:41 +00:00
|
|
|
MakeError(Error, BaseError);
|
2020-06-17 08:26:52 +00:00
|
|
|
MakeError(UsageError, Error);
|
2020-07-20 18:13:37 +00:00
|
|
|
MakeError(UnimplementedError, Error);
|
2020-03-24 20:24:57 +00:00
|
|
|
|
2020-04-26 20:47:41 +00:00
|
|
|
class SysError : public Error
|
2020-04-02 22:02:40 +00:00
|
|
|
{
|
2020-04-26 20:47:41 +00:00
|
|
|
public:
|
|
|
|
int errNo;
|
2020-03-24 15:18:23 +00:00
|
|
|
|
2020-04-26 20:47:41 +00:00
|
|
|
template<typename... Args>
|
2022-07-19 10:49:33 +00:00
|
|
|
SysError(int errNo_, const Args & ... args)
|
2020-06-18 21:25:26 +00:00
|
|
|
: Error("")
|
2020-05-06 20:07:20 +00:00
|
|
|
{
|
2022-07-19 10:49:33 +00:00
|
|
|
errNo = errNo_;
|
2024-03-08 07:10:05 +00:00
|
|
|
auto hf = HintFmt(args...);
|
|
|
|
err.msg = HintFmt("%1%: %2%", Uncolored(hf.str()), strerror(errNo));
|
2020-05-06 20:07:20 +00:00
|
|
|
}
|
2022-07-19 10:49:33 +00:00
|
|
|
|
|
|
|
template<typename... Args>
|
|
|
|
SysError(const Args & ... args)
|
|
|
|
: SysError(errno, args ...)
|
|
|
|
{
|
|
|
|
}
|
2020-04-26 20:47:41 +00:00
|
|
|
};
|
2020-03-27 16:03:02 +00:00
|
|
|
|
2020-03-22 18:25:47 +00:00
|
|
|
}
|