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':
...
This commit is contained in:
Eelco Dolstra 2013-05-16 19:08:02 +02:00
parent 1b3a03f161
commit 18a48d80a0
5 changed files with 43 additions and 8 deletions

View file

@ -247,6 +247,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const
throw TypeError(format(s) % pos % s2); 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)) LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
{ {
throw TypeError(format(s) % 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) { foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
Bindings::iterator j = arg.attrs->find(i->name); Bindings::iterator j = arg.attrs->find(i->name);
if (j == arg.attrs->end()) { if (j == arg.attrs->end()) {
if (!i->def) throwTypeError("function at %1% called without required argument `%2%'", if (!i->def) throwTypeError("%1% called without required argument `%2%'",
fun.lambda.fun->pos, i->name); fun.lambda.fun->showNamePos(), i->name);
env2.values[displ++] = i->def->maybeThunk(*this, env2); env2.values[displ++] = i->def->maybeThunk(*this, env2);
} else { } else {
attrsUsed++; attrsUsed++;
@ -771,7 +776,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
user. */ user. */
foreach (Bindings::iterator, i, *arg.attrs) foreach (Bindings::iterator, i, *arg.attrs)
if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end()) 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 abort(); // can't happen
} }
} }
@ -782,7 +787,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
try { try {
fun.lambda.fun->body->eval(*this, env2, v); fun.lambda.fun->body->eval(*this, env2, v);
} catch (Error & e) { } 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; throw;
} }
} }

View file

@ -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();
}
} }

View file

@ -63,6 +63,7 @@ struct Expr
virtual void bindVars(const StaticEnv & env); virtual void bindVars(const StaticEnv & env);
virtual void eval(EvalState & state, Env & env, Value & v); virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env); virtual Value * maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol & name);
}; };
std::ostream & operator << (std::ostream & str, Expr & e); std::ostream & operator << (std::ostream & str, Expr & e);
@ -197,6 +198,7 @@ struct Formals
struct ExprLambda : Expr struct ExprLambda : Expr
{ {
Pos pos; Pos pos;
Symbol name;
Symbol arg; Symbol arg;
bool matchAttrs; bool matchAttrs;
Formals * formals; Formals * formals;
@ -208,6 +210,8 @@ struct ExprLambda : Expr
throw ParseError(format("duplicate formal function argument `%1%' at %2%") throw ParseError(format("duplicate formal function argument `%1%' at %2%")
% arg % pos); % arg % pos);
}; };
void setName(Symbol & name);
string showNamePos();
COMMON_METHODS COMMON_METHODS
}; };

View file

@ -104,6 +104,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
} }
} }
} }
e->setName(attrPath.back());
} }

View file

@ -49,6 +49,11 @@ public:
return *s; return *s;
} }
bool set() const
{
return s;
}
bool empty() const bool empty() const
{ {
return s->empty(); return s->empty();