nix-super/src/libutil/error.hh

215 lines
4.9 KiB
C++
Raw Normal View History

2020-04-26 23:47:41 +03:00
#pragma once
2020-04-26 23:47:41 +03:00
#include "ref.hh"
2020-04-03 17:48:20 +03:00
#include "types.hh"
2020-07-07 15:37:47 +03:00
#include "fmt.hh"
2020-03-27 18:55:09 +02:00
2020-06-16 02:35:07 +03:00
#include <cstring>
2020-04-26 23:47:41 +03:00
#include <list>
#include <memory>
#include <map>
#include <optional>
2020-07-07 15:37:47 +03:00
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
2020-04-26 23:47:41 +03:00
/* 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 {
/*
2020-06-04 20:53:19 +03:00
2020-06-19 00:25:26 +03:00
This file defines two main structs/classes used in nix error handling.
2020-06-04 20:53:19 +03:00
2020-06-19 00:25:26 +03:00
ErrorInfo provides a standard payload of error information, with conversion to string
happening in the logger rather than at the call site.
2020-06-04 20:53:19 +03:00
2020-06-19 00:25:26 +03:00
BaseError is the ancestor of nix specific exceptions (and Interrupted), and contains
an ErrorInfo.
2020-06-04 20:53:19 +03:00
2020-06-19 00:25:26 +03:00
ErrorInfo structs are sent to the logger as part of an exception, or directly with the
logError or logWarning macros.
2020-03-24 19:21:35 +02:00
2020-06-19 00:25:26 +03:00
See the error-demo.cc program for usage examples.
2020-06-04 20:53:19 +03:00
2020-06-19 00:25:26 +03:00
*/
2020-06-04 20:53:19 +03:00
2020-04-29 06:06:08 +03:00
typedef enum {
2020-04-26 23:47:41 +03:00
lvlError = 0,
lvlWarn,
lvlInfo,
lvlTalkative,
lvlChatty,
lvlDebug,
lvlVomit
} Verbosity;
2020-06-19 00:25:26 +03:00
typedef enum {
2020-05-21 07:18:26 +03:00
foFile,
foStdin,
foString
} FileOrigin;
2020-06-24 17:33:53 +03:00
// the lines of code surrounding an error.
struct LinesOfCode {
std::optional<string> prevLineOfCode;
std::optional<string> errLineOfCode;
std::optional<string> nextLineOfCode;
};
2020-06-04 20:53:19 +03:00
// ErrPos indicates the location of an error in a nix file.
2020-04-29 06:06:08 +03:00
struct ErrPos {
int line = 0;
int column = 0;
2020-04-26 23:47:41 +03:00
string file;
2020-05-21 07:18:26 +03:00
FileOrigin origin;
2020-04-26 23:47:41 +03:00
operator bool() const
{
return line != 0;
}
2020-06-04 20:53:19 +03:00
// convert from the Pos struct, found in libexpr.
2020-04-08 18:07:58 +03:00
template <class P>
ErrPos& operator=(const P &pos)
{
2020-05-21 07:18:26 +03:00
origin = pos.origin;
2020-04-26 23:47:41 +03:00
line = pos.line;
2020-04-08 18:07:58 +03:00
column = pos.column;
2020-06-30 20:00:51 +03:00
// is file symbol null?
if (pos.file.set())
file = pos.file;
else
file = "";
2020-04-08 18:07:58 +03:00
return *this;
}
2020-04-08 18:48:21 +03:00
template <class P>
ErrPos(const P &p)
{
2020-04-28 00:15:08 +03:00
*this = p;
2020-04-08 18:48:21 +03:00
}
};
2020-06-19 00:25:26 +03:00
struct Trace {
2020-06-19 22:44:08 +03:00
std::optional<ErrPos> pos;
2020-06-19 00:25:26 +03:00
hintformat hint;
2020-03-24 19:21:35 +02:00
};
2020-04-29 06:06:08 +03:00
struct ErrorInfo {
2020-04-26 23:47:41 +03:00
Verbosity level;
string name;
2020-10-07 17:33:19 +03:00
string description; // FIXME: remove? it seems to be barely used
2020-04-26 23:47:41 +03:00
std::optional<hintformat> hint;
std::optional<ErrPos> errPos;
2020-06-19 00:25:26 +03:00
std::list<Trace> traces;
2020-03-24 19:21:35 +02:00
2020-04-26 23:47:41 +03:00
static std::optional<string> programName;
2020-04-07 04:43:22 +03:00
};
2020-03-24 19:21:35 +02:00
2020-06-27 21:19:31 +03:00
std::ostream& showErrorInfo(std::ostream &out, const ErrorInfo &einfo, bool showTrace);
2020-03-24 19:21:35 +02:00
2020-04-26 23:47:41 +03:00
/* BaseError should generally not be caught, as it has Interrupted as
a subclass. Catch Error instead. */
class BaseError : public std::exception
2020-04-07 04:43:22 +03:00
{
2020-04-26 23:47:41 +03:00
protected:
2020-05-08 01:43:36 +03:00
mutable ErrorInfo err;
2020-04-30 03:57:05 +03:00
2020-05-08 01:43:36 +03:00
mutable std::optional<string> what_;
2020-05-14 21:28:18 +03:00
const string& calcWhat() const;
2020-04-07 04:43:22 +03:00
public:
2020-04-26 23:47:41 +03:00
unsigned int status = 1; // exit status
template<typename... Args>
BaseError(unsigned int status, const Args & ... args)
2020-06-19 00:25:26 +03:00
: err {.level = lvlError,
.hint = hintfmt(args...)
}
2020-04-26 23:47:41 +03:00
, status(status)
2020-04-30 03:57:05 +03:00
{ }
2020-04-26 23:47:41 +03:00
template<typename... Args>
BaseError(const std::string & fs, const Args & ... args)
2020-06-19 00:25:26 +03:00
: err {.level = lvlError,
.hint = hintfmt(fs, args...)
}
2020-04-30 03:57:05 +03:00
{ }
2020-04-26 23:47:41 +03:00
BaseError(hintformat hint)
2020-06-19 00:25:26 +03:00
: err {.level = lvlError,
.hint = hint
}
{ }
BaseError(ErrorInfo && e)
: err(std::move(e))
{ }
BaseError(const ErrorInfo & e)
2020-04-26 23:47:41 +03:00
: err(e)
2020-04-30 03:57:05 +03:00
{ }
2020-04-29 06:06:08 +03:00
virtual const char* sname() const { return "BaseError"; }
2020-04-26 23:47:41 +03:00
#ifdef EXCEPTION_NEEDS_THROW_SPEC
~BaseError() throw () { };
2020-05-08 01:43:36 +03:00
const char * what() const throw () { return calcWhat().c_str(); }
2020-04-26 23:47:41 +03:00
#else
2020-05-08 01:43:36 +03:00
const char * what() const noexcept override { return calcWhat().c_str(); }
2020-04-26 23:47:41 +03:00
#endif
2020-05-08 01:43:36 +03:00
const string & msg() const { return calcWhat(); }
2020-10-07 17:33:19 +03:00
const ErrorInfo & info() const { calcWhat(); return err; }
2020-06-24 22:10:41 +03:00
2020-06-24 22:46:25 +03:00
template<typename... Args>
BaseError & addTrace(std::optional<ErrPos> e, const string &fs, const Args & ... args)
{
return addTrace(e, hintfmt(fs, args...));
}
2020-06-24 22:10:41 +03:00
BaseError & addTrace(std::optional<ErrPos> e, hintformat hint);
bool hasTrace() const { return !err.traces.empty(); }
2020-04-07 04:43:22 +03:00
};
2020-04-26 23:47:41 +03:00
#define MakeError(newClass, superClass) \
class newClass : public superClass \
{ \
public: \
using superClass::superClass; \
2020-04-29 06:06:08 +03:00
virtual const char* sname() const override { return #newClass; } \
2020-04-26 23:47:41 +03:00
}
2020-04-08 18:07:58 +03:00
2020-04-26 23:47:41 +03:00
MakeError(Error, BaseError);
MakeError(UsageError, Error);
MakeError(UnimplementedError, Error);
2020-04-26 23:47:41 +03:00
class SysError : public Error
2020-04-03 01:02:40 +03:00
{
2020-04-26 23:47:41 +03:00
public:
int errNo;
2020-03-24 17:18:23 +02:00
2020-04-26 23:47:41 +03:00
template<typename... Args>
SysError(const Args & ... args)
2020-06-19 00:25:26 +03:00
: Error("")
2020-05-06 23:07:20 +03:00
{
errNo = errno;
auto hf = hintfmt(args...);
2020-06-02 17:45:37 +03:00
err.hint = hintfmt("%1%: %2%", normaltxt(hf.str()), strerror(errNo));
2020-05-06 23:07:20 +03:00
}
2020-03-27 18:03:02 +02:00
2020-05-06 23:07:20 +03:00
virtual const char* sname() const override { return "SysError"; }
2020-04-26 23:47:41 +03:00
};
2020-03-27 18:03:02 +02:00
}