about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2013-05-16T17·08+0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2013-05-16T17·08+0200
commit18a48d80a0686ba81959057e8becc6272acd6c46 (patch)
treee3f1fabe22a2c2da63c0ea7880cedee6bdb55a87
parent1b3a03f1610b714adca41637ccd85a8157e236ab (diff)
Show function names in error messages
Functions in Nix are anonymous, but if they're assigned to a
variable/attribute, we can use the variable/attribute name in error
messages, e.g.

while evaluating `concatMapStrings' at `/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/pkgs/lib/strings.nix:18:25':
...
-rw-r--r--src/libexpr/eval.cc13
-rw-r--r--src/libexpr/nixexpr.cc20
-rw-r--r--src/libexpr/nixexpr.hh4
-rw-r--r--src/libexpr/parser.y1
-rw-r--r--src/libexpr/symbol-table.hh13
5 files changed, 43 insertions, 8 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 551ed4ba501b..c927db2c10c6 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -247,6 +247,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const
     throw TypeError(format(s) % pos % s2);
 }
 
+LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, const string & s2))
+{
+    throw TypeError(format(s) % s1 % s2);
+}
+
 LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
 {
     throw TypeError(format(s) % pos);
@@ -755,8 +760,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
         foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
             Bindings::iterator j = arg.attrs->find(i->name);
             if (j == arg.attrs->end()) {
-                if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
-                    fun.lambda.fun->pos, i->name);
+                if (!i->def) throwTypeError("%1% called without required argument `%2%'",
+                    fun.lambda.fun->showNamePos(), i->name);
                 env2.values[displ++] = i->def->maybeThunk(*this, env2);
             } else {
                 attrsUsed++;
@@ -771,7 +776,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
                user. */
             foreach (Bindings::iterator, i, *arg.attrs)
                 if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end())
-                    throwTypeError("function at %1% called with unexpected argument `%2%'", fun.lambda.fun->pos, i->name);
+                    throwTypeError("%1% called with unexpected argument `%2%'", fun.lambda.fun->showNamePos(), i->name);
             abort(); // can't happen
         }
     }
@@ -782,7 +787,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
     try {
         fun.lambda.fun->body->eval(*this, env2, v);
     } catch (Error & e) {
-        addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos);
+        addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos());
         throw;
     }
 }
diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc
index c52a413916a7..9b75bb644844 100644
--- a/src/libexpr/nixexpr.cc
+++ b/src/libexpr/nixexpr.cc
@@ -324,4 +324,24 @@ void ExprConcatStrings::bindVars(const StaticEnv & env)
 }
 
 
+/* Storing function names. */
+
+void Expr::setName(Symbol & name)
+{
+}
+
+
+void ExprLambda::setName(Symbol & name)
+{
+    this->name = name;
+    body->setName(name);
+}
+
+
+string ExprLambda::showNamePos()
+{
+    return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str();
+}
+
+
 }
diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh
index 86a7bfd506a0..8256605b5cac 100644
--- a/src/libexpr/nixexpr.hh
+++ b/src/libexpr/nixexpr.hh
@@ -63,6 +63,7 @@ struct Expr
     virtual void bindVars(const StaticEnv & env);
     virtual void eval(EvalState & state, Env & env, Value & v);
     virtual Value * maybeThunk(EvalState & state, Env & env);
+    virtual void setName(Symbol & name);
 };
 
 std::ostream & operator << (std::ostream & str, Expr & e);
@@ -197,6 +198,7 @@ struct Formals
 struct ExprLambda : Expr
 {
     Pos pos;
+    Symbol name;
     Symbol arg;
     bool matchAttrs;
     Formals * formals;
@@ -208,6 +210,8 @@ struct ExprLambda : Expr
             throw ParseError(format("duplicate formal function argument `%1%' at %2%")
                 % arg % pos);
     };
+    void setName(Symbol & name);
+    string showNamePos();
     COMMON_METHODS
 };
 
diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y
index f78780b1d486..964527be6763 100644
--- a/src/libexpr/parser.y
+++ b/src/libexpr/parser.y
@@ -104,6 +104,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
             }
         }
     }
+    e->setName(attrPath.back());
 }
 
 
diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh
index 143fc495b04a..ccdabb0280b4 100644
--- a/src/libexpr/symbol-table.hh
+++ b/src/libexpr/symbol-table.hh
@@ -28,17 +28,17 @@ private:
 
 public:
     Symbol() : s(0) { };
-    
+
     bool operator == (const Symbol & s2) const
     {
         return s == s2.s;
     }
-    
+
     bool operator != (const Symbol & s2) const
     {
         return s != s2.s;
     }
-    
+
     bool operator < (const Symbol & s2) const
     {
         return s < s2.s;
@@ -49,6 +49,11 @@ public:
         return *s;
     }
 
+    bool set() const
+    {
+        return s;
+    }
+
     bool empty() const
     {
         return s->empty();
@@ -66,7 +71,7 @@ inline std::ostream & operator << (std::ostream & str, const Symbol & sym)
 class SymbolTable
 {
 private:
-#if HAVE_TR1_UNORDERED_SET 
+#if HAVE_TR1_UNORDERED_SET
     typedef std::tr1::unordered_set<string> Symbols;
 #else
     typedef std::set<string> Symbols;