Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions doc/manual/rl-next/print-value-in-type-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
synopsis: Type errors include the failing value
issues: #561
prs: #9753
---

In errors like `value is an integer while a list was expected`, the message now
includes the failing value.

Before:

```
error: value is a set while a string was expected
```

After:

```
error: expected a string but found a set: { ghc810 = «thunk»;
ghc8102Binary = «thunk»; ghc8107 = «thunk»; ghc8107Binary = «thunk»;
ghc865Binary = «thunk»; ghc90 = «thunk»; ghc902 = «thunk»; ghc92 = «thunk»;
ghc924Binary = «thunk»; ghc925 = «thunk»; «17 attributes elided»}
```
2 changes: 1 addition & 1 deletion doc/manual/rl-next/source-positions-in-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ error:
| ^
5|

error: value is a set while a string was expected
error: expected a string but found a set
```
4 changes: 2 additions & 2 deletions doc/manual/rl-next/with-error-reporting.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ prs: 9658
Previously an incorrect `with` expression would report no position at all, making it hard to determine where the error originated:

```
nix-repl> with 1; a
nix-repl> with 1; a
error:
… <borked>

Expand All @@ -27,5 +27,5 @@ error:
1| with 1; a
| ^

error: value is an integer while a set was expected
error: expected a set but found an integer
```
11 changes: 9 additions & 2 deletions src/libexpr/eval-inline.hh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
///@file

#include "print.hh"
#include "eval.hh"

namespace nix {
Expand Down Expand Up @@ -114,7 +115,10 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view e
PosIdx pos = getPos();
forceValue(v, pos);
if (v.type() != nAttrs) {
error("value is %1% while a set was expected", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
error("expected a set but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions))
.withTrace(pos, errorCtx).debugThrow<TypeError>();
}
}

Expand All @@ -124,7 +128,10 @@ inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view e
{
forceValue(v, pos);
if (!v.isList()) {
error("value is %1% while a list was expected", showType(v)).withTrace(pos, errorCtx).debugThrow<TypeError>();
error("expected a list but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions))
.withTrace(pos, errorCtx).debugThrow<TypeError>();
}
}

Expand Down
38 changes: 30 additions & 8 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "eval-settings.hh"
#include "hash.hh"
#include "primops.hh"
#include "print-options.hh"
#include "types.hh"
#include "util.hh"
#include "store-api.hh"
Expand Down Expand Up @@ -29,9 +30,9 @@
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <iostream>
#include <fstream>
#include <functional>
#include <iostream>

#include <sys/resource.h>
#include <nlohmann/json.hpp>
Expand Down Expand Up @@ -1153,7 +1154,10 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri
Value v;
e->eval(*this, env, v);
if (v.type() != nBool)
error("value is %1% while a Boolean was expected", showType(v)).withFrame(env, *e).debugThrow<TypeError>();
error("expected a Boolean but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions))
.withFrame(env, *e).debugThrow<TypeError>();
return v.boolean;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
Expand All @@ -1167,7 +1171,10 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx po
try {
e->eval(*this, env, v);
if (v.type() != nAttrs)
error("value is %1% while a set was expected", showType(v)).withFrame(env, *e).debugThrow<TypeError>();
error("expected a set but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions))
.withFrame(env, *e).debugThrow<TypeError>();
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
Expand Down Expand Up @@ -2076,7 +2083,10 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt
try {
forceValue(v, pos);
if (v.type() != nInt)
error("value is %1% while an integer was expected", showType(v)).debugThrow<TypeError>();
error("expected an integer but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions))
.debugThrow<TypeError>();
return v.integer;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
Expand All @@ -2092,7 +2102,10 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err
if (v.type() == nInt)
return v.integer;
else if (v.type() != nFloat)
error("value is %1% while a float was expected", showType(v)).debugThrow<TypeError>();
error("expected a float but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions))
.debugThrow<TypeError>();
return v.fpoint;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
Expand All @@ -2106,7 +2119,10 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx
try {
forceValue(v, pos);
if (v.type() != nBool)
error("value is %1% while a Boolean was expected", showType(v)).debugThrow<TypeError>();
error("expected a Boolean but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions))
.debugThrow<TypeError>();
return v.boolean;
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
Expand All @@ -2126,7 +2142,10 @@ void EvalState::forceFunction(Value & v, const PosIdx pos, std::string_view erro
try {
forceValue(v, pos);
if (v.type() != nFunction && !isFunctor(v))
error("value is %1% while a function was expected", showType(v)).debugThrow<TypeError>();
error("expected a function but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions))
.debugThrow<TypeError>();
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
throw;
Expand All @@ -2139,7 +2158,10 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
try {
forceValue(v, pos);
if (v.type() != nString)
error("value is %1% while a string was expected", showType(v)).debugThrow<TypeError>();
error("expected a string but found %1%: %2%",
showType(v),
ValuePrinter(*this, v, errorPrintOptions))
.debugThrow<TypeError>();
return v.string_view();
} catch (Error & e) {
e.addTrace(positions[pos], errorCtx);
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,7 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu
if (args[0]->type() == nString)
printError("trace: %1%", args[0]->string_view());
else
printError("trace: %1%", printValue(state, *args[0]));
printError("trace: %1%", ValuePrinter(state, *args[0]));
state.forceValue(*args[1], pos);
v = *args[1];
}
Expand Down
1 change: 1 addition & 0 deletions src/libexpr/print-ambiguous.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "print-ambiguous.hh"
#include "print.hh"
#include "signals.hh"
#include "eval.hh"

namespace nix {

Expand Down
12 changes: 12 additions & 0 deletions src/libexpr/print-options.hh
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,16 @@ struct PrintOptions
size_t maxStringLength = std::numeric_limits<size_t>::max();
};

/**
* `PrintOptions` for unknown and therefore potentially large values in error messages,
* to avoid printing "too much" output.
*/
static PrintOptions errorPrintOptions = PrintOptions {
.ansiColors = true,
.maxDepth = 10,
.maxAttrs = 10,
.maxListItems = 10,
.maxStringLength = 1024
};

}
7 changes: 7 additions & 0 deletions src/libexpr/print.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "store-api.hh"
#include "terminal.hh"
#include "english.hh"
#include "eval.hh"

namespace nix {

Expand Down Expand Up @@ -501,4 +502,10 @@ void printValue(EvalState & state, std::ostream & output, Value & v, PrintOption
Printer(output, state, options).print(v);
}

std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer)
{
printValue(printer.state, output, printer.value, printer.options);
return output;
}

}
21 changes: 20 additions & 1 deletion src/libexpr/print.hh
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@

#include <iostream>

#include "eval.hh"
#include "print-options.hh"

namespace nix {

class EvalState;
struct Value;

/**
* Print a string as a Nix string literal.
*
Expand Down Expand Up @@ -59,4 +61,21 @@ std::ostream & printIdentifier(std::ostream & o, std::string_view s);

void printValue(EvalState & state, std::ostream & str, Value & v, PrintOptions options = PrintOptions {});

/**
* A partially-applied form of `printValue` which can be formatted using `<<`
* without allocating an intermediate string.
*/
class ValuePrinter {
friend std::ostream & operator << (std::ostream & output, const ValuePrinter & printer);
private:
EvalState & state;
Value & value;
PrintOptions options;

public:
ValuePrinter(EvalState & state, Value & value, PrintOptions options = PrintOptions {})
: state(state), value(value), options(options) { }
};

std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
}
4 changes: 2 additions & 2 deletions src/libutil/error.cc
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
* try {
* e->eval(*this, env, v);
* if (v.type() != nAttrs)
* throwTypeError("value is %1% while a set was expected", v);
* throwTypeError("expected a set but found %1%", v);
* } catch (Error & e) {
* e.addTrace(pos, errorCtx);
* throw;
Expand All @@ -349,7 +349,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
* e->eval(*this, env, v);
* try {
* if (v.type() != nAttrs)
* throwTypeError("value is %1% while a set was expected", v);
* throwTypeError("expected a set but found %1%", v);
* } catch (Error & e) {
* e.addTrace(pos, errorCtx);
* throw;
Expand Down
2 changes: 1 addition & 1 deletion src/nix/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption

else {
state->forceValueDeep(*v);
logger->cout("%s", printValue(*state, *v));
logger->cout("%s", ValuePrinter(*state, *v, PrintOptions { .force = true }));
}
}
};
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/dyn-drv/eval-outputOf.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ nix --experimental-features 'nix-command' eval --impure --expr \
# resolve first. Adding a test so we don't liberalise it by accident.
expectStderr 1 nix --experimental-features 'nix-command dynamic-derivations' eval --impure --expr \
'builtins.outputOf (import ../dependencies.nix {}) "out"' \
| grepQuiet "value is a set while a string was expected"
| grepQuiet "expected a string but found a set"

# Test that "DrvDeep" string contexts are not supported at this time
#
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/lang/eval-fail-attr-name-type.err.exp
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ error:
| ^
8|

error: value is an integer while a string was expected
error: expected a string but found an integer: 1
2 changes: 1 addition & 1 deletion tests/functional/lang/eval-fail-call-primop.err.exp
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ error:

… while evaluating the first argument passed to builtins.length

error: value is an integer while a list was expected
error: expected a list but found an integer: 1
2 changes: 1 addition & 1 deletion tests/functional/lang/eval-fail-list.err.exp
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ error:
| ^
2|

error: value is an integer while a list was expected
error: expected a list but found an integer: 8
2 changes: 1 addition & 1 deletion tests/functional/lang/eval-fail-set-override.err.exp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error:
… while evaluating the `__overrides` attribute

error: value is an integer while a set was expected
error: expected a set but found an integer: 1
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ error:
| ^
6|

error: value is a set while a string was expected
error: expected a string but found a set: { }
Loading