aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2020-04-22 15:29:10 +0200
committerEelco Dolstra <edolstra@gmail.com>2020-04-22 15:29:10 +0200
commit16e3bf453782eebb2270c9318b51f3ba2ad993fc (patch)
treee27912f39e693443ae4d9c60faabe5cbe87bff96
parent2ea4d45449ea676e27cc678145ef39af7ac05ca8 (diff)
parent12814806efca65a73aebf8d3109eb2fbf880e2b8 (diff)
Merge branch 'error-format' of https://github.com/bburdette/nix
-rw-r--r--.gitignore2
-rw-r--r--Makefile3
-rw-r--r--src/error-demo/error-demo.cc66
-rw-r--r--src/error-demo/local.mk12
-rw-r--r--src/libutil/ansicolor.hh13
-rw-r--r--src/libutil/error.cc146
-rw-r--r--src/libutil/error.hh121
-rw-r--r--src/libutil/types.hh10
-rw-r--r--src/libutil/util.hh11
9 files changed, 370 insertions, 14 deletions
diff --git a/.gitignore b/.gitignore
index e3186fa76..ad5684123 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,6 +75,8 @@ perl/Makefile.config
/src/nix-copy-closure/nix-copy-closure
+/src/error-demo/error-demo
+
/src/build-remote/build-remote
# /tests/
diff --git a/Makefile b/Makefile
index e3057c36c..f6b4a3101 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,8 @@ makefiles = \
misc/upstart/local.mk \
doc/manual/local.mk \
tests/local.mk \
- tests/plugins/local.mk
+ tests/plugins/local.mk \
+ src/error-demo/local.mk
-include Makefile.config
diff --git a/src/error-demo/error-demo.cc b/src/error-demo/error-demo.cc
new file mode 100644
index 000000000..a9ff6057c
--- /dev/null
+++ b/src/error-demo/error-demo.cc
@@ -0,0 +1,66 @@
+#include "error.hh"
+#include "nixexpr.hh"
+
+#include <iostream>
+#include <optional>
+
+int main()
+{
+ using namespace nix;
+
+ // In each program where errors occur, this has to be set.
+ ErrorInfo::programName = std::optional("error-demo");
+
+ // Error in a program; no hint and no nix code.
+ printErrorInfo(
+ ErrorInfo { .level = elError,
+ .name = "name",
+ .description = "error description",
+ });
+
+ // Warning with name, description, and hint.
+ // The hintfmt function makes all the substituted text yellow.
+ printErrorInfo(
+ ErrorInfo { .level = elWarning,
+ .name = "name",
+ .description = "error description",
+ .hint = std::optional(
+ hintfmt("there was a %1%", "warning")),
+ });
+
+
+ // Warning with nix file, line number, column, and the lines of
+ // code where a warning occurred.
+ SymbolTable testTable;
+ auto problem_file = testTable.create("myfile.nix");
+
+ printErrorInfo(
+ ErrorInfo{
+ .level = elWarning,
+ .name = "warning name",
+ .description = "warning description",
+ .hint = hintfmt("this hint has %1% templated %2%!!", "yellow", "values"),
+ .nixCode = NixCode {
+ .errPos = Pos(problem_file, 40, 13),
+ .prevLineOfCode = std::nullopt,
+ .errLineOfCode = "this is the problem line of code",
+ .nextLineOfCode = std::nullopt
+ }});
+
+ // Error with previous and next lines of code.
+ printErrorInfo(
+ ErrorInfo{
+ .level = elError,
+ .name = "error name",
+ .description = "error description",
+ .hint = hintfmt("this hint has %1% templated %2%!!", "yellow", "values"),
+ .nixCode = NixCode {
+ .errPos = Pos(problem_file, 40, 13),
+ .prevLineOfCode = std::optional("previous line of code"),
+ .errLineOfCode = "this is the problem line of code",
+ .nextLineOfCode = std::optional("next line of code"),
+ }});
+
+
+ return 0;
+}
diff --git a/src/error-demo/local.mk b/src/error-demo/local.mk
new file mode 100644
index 000000000..2c528490a
--- /dev/null
+++ b/src/error-demo/local.mk
@@ -0,0 +1,12 @@
+programs += error-demo
+
+error-demo_DIR := $(d)
+
+error-demo_SOURCES := \
+ $(wildcard $(d)/*.cc) \
+
+error-demo_CXXFLAGS += -I src/libutil -I src/libexpr
+
+error-demo_LIBS = libutil libexpr
+
+error-demo_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) -lboost_context -lboost_thread -lboost_system
diff --git a/src/libutil/ansicolor.hh b/src/libutil/ansicolor.hh
new file mode 100644
index 000000000..390bd4d17
--- /dev/null
+++ b/src/libutil/ansicolor.hh
@@ -0,0 +1,13 @@
+#pragma once
+
+namespace nix
+{
+ /* Some ANSI escape sequences. */
+ #define ANSI_NORMAL "\e[0m"
+ #define ANSI_BOLD "\e[1m"
+ #define ANSI_FAINT "\e[2m"
+ #define ANSI_RED "\e[31;1m"
+ #define ANSI_GREEN "\e[32;1m"
+ #define ANSI_YELLOW "\e[33;1m"
+ #define ANSI_BLUE "\e[34;1m"
+}
diff --git a/src/libutil/error.cc b/src/libutil/error.cc
new file mode 100644
index 000000000..a5571d4ec
--- /dev/null
+++ b/src/libutil/error.cc
@@ -0,0 +1,146 @@
+#include "error.hh"
+
+#include <iostream>
+#include <optional>
+
+namespace nix
+{
+
+std::optional<string> ErrorInfo::programName = std::nullopt;
+
+std::ostream& operator<<(std::ostream &os, const hintformat &hf)
+{
+ return os << hf.str();
+}
+
+string showErrPos(const ErrPos &errPos)
+{
+ if (errPos.column > 0) {
+ return fmt("(%1%:%2%)", errPos.lineNumber, errPos.column);
+ } else {
+ return fmt("(%1%)", errPos.lineNumber);
+ };
+}
+
+void printCodeLines(const string &prefix, const NixCode &nixCode)
+{
+ // previous line of code.
+ if (nixCode.prevLineOfCode.has_value()) {
+ std::cout << fmt("%1% %|2$5d|| %3%",
+ prefix,
+ (nixCode.errPos.lineNumber - 1),
+ *nixCode.prevLineOfCode)
+ << std::endl;
+ }
+
+ // line of code containing the error.%2$+5d%
+ std::cout << fmt("%1% %|2$5d|| %3%",
+ prefix,
+ (nixCode.errPos.lineNumber),
+ nixCode.errLineOfCode)
+ << std::endl;
+
+ // error arrows for the column range.
+ if (nixCode.errPos.column > 0) {
+ int start = nixCode.errPos.column;
+ std::string spaces;
+ for (int i = 0; i < start; ++i) {
+ spaces.append(" ");
+ }
+
+ std::string arrows("^");
+
+ std::cout << fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL,
+ prefix,
+ spaces,
+ arrows) << std::endl;
+ }
+
+ // next line of code.
+ if (nixCode.nextLineOfCode.has_value()) {
+ std::cout << fmt("%1% %|2$5d|| %3%",
+ prefix,
+ (nixCode.errPos.lineNumber + 1),
+ *nixCode.nextLineOfCode)
+ << std::endl;
+ }
+}
+
+void printErrorInfo(const ErrorInfo &einfo)
+{
+ int errwidth = 80;
+ string prefix = " ";
+
+ string levelString;
+ switch (einfo.level) {
+ case ErrLevel::elError: {
+ levelString = ANSI_RED;
+ levelString += "error:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ case ErrLevel::elWarning: {
+ levelString = ANSI_YELLOW;
+ levelString += "warning:";
+ levelString += ANSI_NORMAL;
+ break;
+ }
+ default: {
+ levelString = fmt("invalid error level: %1%", einfo.level);
+ break;
+ }
+ }
+
+ int ndl = prefix.length() + levelString.length() + 3 + einfo.name.length() + einfo.programName.value_or("").length();
+ int dashwidth = ndl > (errwidth - 3) ? 3 : errwidth - ndl;
+
+ string dashes;
+ for (int i = 0; i < dashwidth; ++i)
+ dashes.append("-");
+
+ // divider.
+ std::cout << fmt("%1%%2%" ANSI_BLUE " %3% %4% %5% %6%" ANSI_NORMAL,
+ prefix,
+ levelString,
+ "---",
+ einfo.name,
+ dashes,
+ einfo.programName.value_or(""))
+ << std::endl;
+
+ // filename.
+ if (einfo.nixCode.has_value()) {
+ if (einfo.nixCode->errPos.nixFile != "") {
+ string eline = einfo.nixCode->errLineOfCode != ""
+ ? string(" ") + showErrPos(einfo.nixCode->errPos)
+ : "";
+
+ std::cout << fmt("%1%in file: " ANSI_BLUE "%2%%3%" ANSI_NORMAL,
+ prefix,
+ einfo.nixCode->errPos.nixFile,
+ eline) << std::endl;
+ std::cout << prefix << std::endl;
+ } else {
+ std::cout << fmt("%1%from command line argument", prefix) << std::endl;
+ std::cout << prefix << std::endl;
+ }
+ }
+
+ // description
+ std::cout << prefix << einfo.description << std::endl;
+ std::cout << prefix << std::endl;
+
+ // lines of code.
+ if (einfo.nixCode->errLineOfCode != "") {
+ printCodeLines(prefix, *einfo.nixCode);
+ std::cout << prefix << std::endl;
+ }
+
+ // hint
+ if (einfo.hint.has_value()) {
+ std::cout << prefix << *einfo.hint << std::endl;
+ std::cout << prefix << std::endl;
+ }
+}
+
+}
diff --git a/src/libutil/error.hh b/src/libutil/error.hh
new file mode 100644
index 000000000..f402b692e
--- /dev/null
+++ b/src/libutil/error.hh
@@ -0,0 +1,121 @@
+#ifndef error_hh
+#define error_hh
+
+#include "ansicolor.hh"
+#include <string>
+#include <optional>
+#include <iostream>
+#include "types.hh"
+
+namespace nix
+{
+
+typedef enum {
+ elWarning,
+ elError
+} ErrLevel;
+
+struct ErrPos
+{
+ int lineNumber;
+ int column;
+ string nixFile;
+
+ template <class P>
+ ErrPos& operator=(const P &pos)
+ {
+ lineNumber = pos.line;
+ column = pos.column;
+ nixFile = pos.file;
+ return *this;
+ }
+
+ template <class P>
+ ErrPos(const P &p)
+ {
+ *this = p;
+ }
+};
+
+struct NixCode
+{
+ ErrPos errPos;
+ std::optional<string> prevLineOfCode;
+ string errLineOfCode;
+ std::optional<string> nextLineOfCode;
+};
+
+// ----------------------------------------------------------------
+// format function for hints. same as fmt, except templated values
+// are always in yellow.
+
+template <class T>
+struct yellowify
+{
+ yellowify(T &s) : value(s) {}
+ T &value;
+};
+
+template <class T>
+std::ostream& operator<<(std::ostream &out, const yellowify<T> &y)
+{
+ return out << ANSI_YELLOW << y.value << ANSI_NORMAL;
+}
+
+class hintformat
+{
+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;
+ }
+
+ std::string str() const
+ {
+ return fmt.str();
+ }
+
+ template <typename U>
+ friend class AddHint;
+private:
+ format fmt;
+};
+
+std::ostream& operator<<(std::ostream &os, const hintformat &hf);
+
+template<typename... Args>
+inline hintformat hintfmt(const std::string & fs, const Args & ... args)
+{
+ hintformat f(fs);
+ formatHelper(f, args...);
+ return f;
+}
+
+// -------------------------------------------------
+// ErrorInfo.
+struct ErrorInfo
+{
+ ErrLevel level;
+ string name;
+ string description;
+ std::optional<hintformat> hint;
+ std::optional<NixCode> nixCode;
+
+ static std::optional<string> programName;
+};
+
+// --------------------------------------------------------
+// error printing
+
+// just to cout for now.
+void printErrorInfo(const ErrorInfo &einfo);
+
+}
+
+#endif
diff --git a/src/libutil/types.hh b/src/libutil/types.hh
index a1ce7b372..250c9581d 100644
--- a/src/libutil/types.hh
+++ b/src/libutil/types.hh
@@ -41,7 +41,8 @@ struct FormatOrString
{
string s;
FormatOrString(const string & s) : s(s) { };
- FormatOrString(const format & f) : s(f.str()) { };
+ template<class F>
+ FormatOrString(const F & f) : s(f.str()) { };
FormatOrString(const char * s) : s(s) { };
};
@@ -51,12 +52,13 @@ struct FormatOrString
... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion
takes place). */
-inline void formatHelper(boost::format & f)
+template<class F>
+inline void formatHelper(F & f)
{
}
-template<typename T, typename... Args>
-inline void formatHelper(boost::format & f, const T & x, const Args & ... args)
+template<class F, typename T, typename... Args>
+inline void formatHelper(F & f, const T & x, const Args & ... args)
{
formatHelper(f % x, args...);
}
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 815b1f288..8e24ef968 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -2,6 +2,8 @@
#include "types.hh"
#include "logging.hh"
+#include "ansicolor.hh"
+#include "error.hh"
#include <sys/types.h>
#include <sys/stat.h>
@@ -446,15 +448,6 @@ std::string shellEscape(const std::string & s);
void ignoreException();
-/* Some ANSI escape sequences. */
-#define ANSI_NORMAL "\e[0m"
-#define ANSI_BOLD "\e[1m"
-#define ANSI_FAINT "\e[2m"
-#define ANSI_RED "\e[31;1m"
-#define ANSI_GREEN "\e[32;1m"
-#define ANSI_YELLOW "\e[33;1m"
-#define ANSI_BLUE "\e[34;1m"
-
/* Tree formatting. */
constexpr char treeConn[] = "├───";