diff options
Diffstat (limited to 'src/libutil/error.hh')
-rw-r--r-- | src/libutil/error.hh | 225 |
1 files changed, 145 insertions, 80 deletions
diff --git a/src/libutil/error.hh b/src/libutil/error.hh index f402b692e..1e6102ce1 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -1,121 +1,186 @@ -#ifndef error_hh -#define error_hh +#pragma once -#include "ansicolor.hh" -#include <string> -#include <optional> -#include <iostream> + +#include "ref.hh" #include "types.hh" -namespace nix -{ +#include <cstring> +#include <list> +#include <memory> +#include <map> +#include <optional> -typedef enum { - elWarning, - elError -} ErrLevel; +#include "fmt.hh" -struct ErrPos -{ - int lineNumber; - int column; - string nixFile; +/* Before 4.7, gcc's std::exception uses empty throw() specifiers for + * its (virtual) destructor and what() in c++11 mode, in violation of spec + */ +#ifdef __GNUC__ +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7) +#define EXCEPTION_NEEDS_THROW_SPEC +#endif +#endif + +namespace nix { + +/* + +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 the error-demo.cc program for usage examples. + +*/ + +typedef enum { + lvlError = 0, + lvlWarn, + lvlInfo, + lvlTalkative, + lvlChatty, + lvlDebug, + lvlVomit +} Verbosity; + +// ErrPos indicates the location of an error in a nix file. +struct ErrPos { + int line = 0; + int column = 0; + string file; + + operator bool() const + { + return line != 0; + } + + // convert from the Pos struct, found in libexpr. template <class P> ErrPos& operator=(const P &pos) { - lineNumber = pos.line; + line = pos.line; column = pos.column; - nixFile = pos.file; + file = pos.file; return *this; } template <class P> ErrPos(const P &p) { - *this = p; + *this = p; } }; -struct NixCode -{ +struct NixCode { ErrPos errPos; std::optional<string> prevLineOfCode; - string errLineOfCode; + std::optional<string> errLineOfCode; std::optional<string> nextLineOfCode; }; -// ---------------------------------------------------------------- -// format function for hints. same as fmt, except templated values -// are always in yellow. +struct ErrorInfo { + Verbosity level; + string name; + string description; + std::optional<hintformat> hint; + std::optional<NixCode> nixCode; -template <class T> -struct yellowify -{ - yellowify(T &s) : value(s) {} - T &value; + static std::optional<string> programName; }; -template <class T> -std::ostream& operator<<(std::ostream &out, const yellowify<T> &y) -{ - return out << ANSI_YELLOW << y.value << ANSI_NORMAL; -} +std::ostream& operator<<(std::ostream &out, const ErrorInfo &einfo); -class hintformat +/* BaseError should generally not be caught, as it has Interrupted as + a subclass. Catch Error instead. */ +class BaseError : public std::exception { +protected: + string prefix_; // used for location traces etc. + mutable ErrorInfo err; + + mutable std::optional<string> what_; + const string& calcWhat() const; + public: - hintformat(string format) :fmt(format) - { - fmt.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit); - } - template<class T> - hintformat& operator%(const T &value) - { - fmt % yellowify(value); - return *this; - } + unsigned int status = 1; // exit status + + template<typename... Args> + BaseError(unsigned int status, const Args & ... 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...) + } + { } + + BaseError(hintformat hint) + : err { .level = lvlError, + .hint = hint + } + { } + + BaseError(ErrorInfo && e) + : err(std::move(e)) + { } + + BaseError(const ErrorInfo & e) + : err(e) + { } + + virtual const char* sname() const { return "BaseError"; } + +#ifdef EXCEPTION_NEEDS_THROW_SPEC + ~BaseError() throw () { }; + const char * what() const throw () { return calcWhat().c_str(); } +#else + const char * what() const noexcept override { return calcWhat().c_str(); } +#endif - std::string str() const - { - return fmt.str(); - } + const string & msg() const { return calcWhat(); } + const string & prefix() const { return prefix_; } + BaseError & addPrefix(const FormatOrString & fs); - template <typename U> - friend class AddHint; -private: - format fmt; + const ErrorInfo & info() { calcWhat(); return err; } }; -std::ostream& operator<<(std::ostream &os, const hintformat &hf); +#define MakeError(newClass, superClass) \ + class newClass : public superClass \ + { \ + public: \ + using superClass::superClass; \ + virtual const char* sname() const override { return #newClass; } \ + } -template<typename... Args> -inline hintformat hintfmt(const std::string & fs, const Args & ... args) -{ - hintformat f(fs); - formatHelper(f, args...); - return f; -} +MakeError(Error, BaseError); -// ------------------------------------------------- -// ErrorInfo. -struct ErrorInfo +class SysError : public Error { - ErrLevel level; - string name; - string description; - std::optional<hintformat> hint; - std::optional<NixCode> nixCode; - - static std::optional<string> programName; -}; +public: + int errNo; -// -------------------------------------------------------- -// error printing + template<typename... Args> + SysError(const Args & ... args) + :Error("") + { + errNo = errno; + auto hf = hintfmt(args...); + err.hint = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo)); + } -// just to cout for now. -void printErrorInfo(const ErrorInfo &einfo); + virtual const char* sname() const override { return "SysError"; } +}; } - -#endif |