toJSON: report error position for fancier output

Given flake:

```nix
{ description = "nix json error provenance";
  inputs = {};
  outputs = { self }: {
    jsonFunction = _: "function";
    json = builtins.toJSON (_: "function");
  };
}

```
- Before:

```console
❯ nix eval --json .#jsonFunction
error: cannot convert a function to JSON
```

- After:

```console
❯ nix eval --json .#jsonFunction
error: cannot convert a function to JSON

       at /nix/store/b7imf1c2j4jnkg3ys7fsfbj02s5j0i4f-source/testflake/flake.nix:4:5:

            3|   outputs = { self }: {
            4|     jsonFunction = _: "function";
             |     ^
            5|     json = builtins.toJSON (_: "function");
```
This commit is contained in:
Shay Bergmann 2021-10-25 21:13:35 +00:00
parent 4a2b7cc68c
commit ba81e871b2
No known key found for this signature in database
GPG key ID: 4D33B73FFDB9217C
6 changed files with 22 additions and 16 deletions

View file

@ -1008,7 +1008,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
if (i->name == state.sStructuredAttrs) continue; if (i->name == state.sStructuredAttrs) continue;
auto placeholder(jsonObject->placeholder(key)); auto placeholder(jsonObject->placeholder(key));
printValueAsJSON(state, true, *i->value, placeholder, context); printValueAsJSON(state, true, *i->value, pos, placeholder, context);
if (i->name == state.sBuilder) if (i->name == state.sBuilder)
drv.builder = state.forceString(*i->value, context, posDrvName); drv.builder = state.forceString(*i->value, context, posDrvName);
@ -1687,7 +1687,7 @@ static void prim_toJSON(EvalState & state, const Pos & pos, Value * * args, Valu
{ {
std::ostringstream out; std::ostringstream out;
PathSet context; PathSet context;
printValueAsJSON(state, true, *args[0], out, context); printValueAsJSON(state, true, *args[0], pos, out, context);
mkString(v, out.str(), context); mkString(v, out.str(), context);
} }

View file

@ -10,11 +10,11 @@
namespace nix { namespace nix {
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, JSONPlaceholder & out, PathSet & context) Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context)
{ {
checkInterrupt(); checkInterrupt();
if (strict) state.forceValue(v); if (strict) state.forceValue(v, pos);
switch (v.type()) { switch (v.type()) {
@ -54,10 +54,10 @@ void printValueAsJSON(EvalState & state, bool strict,
for (auto & j : names) { for (auto & j : names) {
Attr & a(*v.attrs->find(state.symbols.create(j))); Attr & a(*v.attrs->find(state.symbols.create(j)));
auto placeholder(obj.placeholder(j)); auto placeholder(obj.placeholder(j));
printValueAsJSON(state, strict, *a.value, placeholder, context); printValueAsJSON(state, strict, *a.value, *a.pos, placeholder, context);
} }
} else } else
printValueAsJSON(state, strict, *i->value, out, context); printValueAsJSON(state, strict, *i->value, *i->pos, out, context);
break; break;
} }
@ -65,7 +65,7 @@ void printValueAsJSON(EvalState & state, bool strict,
auto list(out.list()); auto list(out.list());
for (unsigned int n = 0; n < v.listSize(); ++n) { for (unsigned int n = 0; n < v.listSize(); ++n) {
auto placeholder(list.placeholder()); auto placeholder(list.placeholder());
printValueAsJSON(state, strict, *v.listElems()[n], placeholder, context); printValueAsJSON(state, strict, *v.listElems()[n], noPos, placeholder, context);
} }
break; break;
} }
@ -79,18 +79,24 @@ void printValueAsJSON(EvalState & state, bool strict,
break; break;
case nThunk: case nThunk:
throw TypeError("cannot convert %1% to JSON", showType(v)); throw TypeError({
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
.errPos = pos
});
case nFunction: case nFunction:
throw TypeError("cannot convert %1% to JSON", showType(v)); throw TypeError({
.msg = hintfmt("cannot convert %1% to JSON", showType(v)),
.errPos = pos
});
} }
} }
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, std::ostream & str, PathSet & context) Value & v, const Pos & pos, std::ostream & str, PathSet & context)
{ {
JSONPlaceholder out(str); JSONPlaceholder out(str);
printValueAsJSON(state, strict, v, out, context); printValueAsJSON(state, strict, v, pos, out, context);
} }
void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict, void ExternalValueBase::printValueAsJSON(EvalState & state, bool strict,

View file

@ -11,9 +11,9 @@ namespace nix {
class JSONPlaceholder; class JSONPlaceholder;
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, JSONPlaceholder & out, PathSet & context); Value & v, const Pos & pos, JSONPlaceholder & out, PathSet & context);
void printValueAsJSON(EvalState & state, bool strict, void printValueAsJSON(EvalState & state, bool strict,
Value & v, std::ostream & str, PathSet & context); Value & v, const Pos & pos, std::ostream & str, PathSet & context);
} }

View file

@ -879,7 +879,7 @@ static void queryJSON(Globals & globals, vector<DrvInfo> & elems)
placeholder.write(nullptr); placeholder.write(nullptr);
} else { } else {
PathSet context; PathSet context;
printValueAsJSON(*globals.state, true, *v, placeholder, context); printValueAsJSON(*globals.state, true, *v, noPos, placeholder, context);
} }
} }
} }

View file

@ -52,7 +52,7 @@ void processExpr(EvalState & state, const Strings & attrPaths,
if (output == okXML) if (output == okXML)
printValueAsXML(state, strict, location, vRes, std::cout, context); printValueAsXML(state, strict, location, vRes, std::cout, context);
else if (output == okJSON) else if (output == okJSON)
printValueAsJSON(state, strict, vRes, std::cout, context); printValueAsJSON(state, strict, vRes, noPos, std::cout, context);
else { else {
if (strict) state.forceValueDeep(vRes); if (strict) state.forceValueDeep(vRes);
std::cout << vRes << std::endl; std::cout << vRes << std::endl;

View file

@ -112,7 +112,7 @@ struct CmdEval : MixJSON, InstallableCommand
else if (json) { else if (json) {
JSONPlaceholder jsonOut(std::cout); JSONPlaceholder jsonOut(std::cout);
printValueAsJSON(*state, true, *v, jsonOut, context); printValueAsJSON(*state, true, *v, pos, jsonOut, context);
} }
else { else {