distinguish between throws & errors during throw
Turns errors like this: let throwMsg = a: throw (a + " invalid bar"); in throwMsg "bullshit" error: … from call site at «string»:3:4: 2| throwMsg = a: throw (a + " invalid bar"); 3| in throwMsg "bullshit" | ^ … while calling 'throwMsg' at «string»:2:14: 1| let 2| throwMsg = a: throw (a + " invalid bar"); | ^ 3| in throwMsg "bullshit" … while calling the 'throw' builtin at «string»:2:17: 1| let 2| throwMsg = a: throw (a + " invalid bar"); | ^ 3| in throwMsg "bullshit" error: bullshit invalid bar into errors like this: let throwMsg = a: throw (a + " invalid bar"); in throwMsg "bullshit" error: … from call site at «string»:3:4: 2| throwMsg = a: throw (a + " invalid bar"); 3| in throwMsg "bullshit" | ^ … while calling 'throwMsg' at «string»:2:14: 1| let 2| throwMsg = a: throw (a + " invalid bar"); | ^ 3| in throwMsg "bullshit" … caused by explicit throw at «string»:2:17: 1| let 2| throwMsg = a: throw (a + " invalid bar"); | ^ 3| in throwMsg "bullshit" error: bullshit invalid bar Change-Id: I593688928ece20f97999d1bf03b2b46d9ac338cb
This commit is contained in:
parent
14bf54bd39
commit
4c7165be86
10 changed files with 111 additions and 6 deletions
70
doc/manual/rl-next/distinguish-throw-errors.md
Normal file
70
doc/manual/rl-next/distinguish-throw-errors.md
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
---
|
||||||
|
synopsis: "Distinguish between explicit throws and errors that happened while evaluating a throw"
|
||||||
|
cls: 1511
|
||||||
|
credits: Qyriad
|
||||||
|
category: Improvements
|
||||||
|
---
|
||||||
|
|
||||||
|
Previously, errors caused by an expression like `throw "invalid argument"` were treated like an error that happened simply while some builtin function was being called:
|
||||||
|
|
||||||
|
```
|
||||||
|
let
|
||||||
|
throwMsg = p: throw "${p} isn't the right package";
|
||||||
|
in throwMsg "linuz"
|
||||||
|
|
||||||
|
error:
|
||||||
|
… while calling the 'throw' builtin
|
||||||
|
at «string»:2:17:
|
||||||
|
1| let
|
||||||
|
2| throwMsg = p: throw "${p} isn't the right package";
|
||||||
|
| ^
|
||||||
|
3| in throwMsg "linuz"
|
||||||
|
|
||||||
|
error: linuz isn't the right package
|
||||||
|
```
|
||||||
|
|
||||||
|
But the error didn't just happen "while" calling the `throw` builtin — it's a throw error!
|
||||||
|
Now it looks like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
let
|
||||||
|
throwMsg = p: throw "${p} isn't the right package";
|
||||||
|
in throwMsg "linuz"
|
||||||
|
|
||||||
|
error:
|
||||||
|
… caused by explicit throw
|
||||||
|
at «string»:2:17:
|
||||||
|
1| let
|
||||||
|
2| throwMsg = p: throw "${p} isn't the right package";
|
||||||
|
| ^
|
||||||
|
3| in throwMsg "linuz"
|
||||||
|
|
||||||
|
error: linuz isn't the right package
|
||||||
|
```
|
||||||
|
|
||||||
|
This also means that incorrect usage of `throw` or errors evaluating its arguments are easily distinguishable from explicit throws:
|
||||||
|
|
||||||
|
```
|
||||||
|
let
|
||||||
|
throwMsg = p: throw "${p} isn't the right package";
|
||||||
|
in throwMsg { attrs = "error when coerced in string interpolation"; }
|
||||||
|
|
||||||
|
error:
|
||||||
|
… while calling the 'throw' builtin
|
||||||
|
at «string»:2:17:
|
||||||
|
1| let
|
||||||
|
2| throwMsg = p: throw "${p} isn't the right package";
|
||||||
|
| ^
|
||||||
|
3| in throwMsg { attrs = "error when coerced in string interpolation"; }
|
||||||
|
|
||||||
|
… while evaluating a path segment
|
||||||
|
at «string»:2:24:
|
||||||
|
1| let
|
||||||
|
2| throwMsg = p: throw "${p} isn't the right package";
|
||||||
|
| ^
|
||||||
|
3| in throwMsg { attrs = "error when coerced in string interpolation"; }
|
||||||
|
|
||||||
|
error: cannot coerce a set to a string: { attrs = "error when coerced in string interpolation"; }
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, instead of an actual thrown error, a type error happens first (trying to coerce an attribute set to a string), but that type error happened *while* calling `throw`.
|
|
@ -1807,6 +1807,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
||||||
|
|
||||||
try {
|
try {
|
||||||
fn->fun(*this, vCur.determinePos(noPos), args, vCur);
|
fn->fun(*this, vCur.determinePos(noPos), args, vCur);
|
||||||
|
} catch (ThrownError & e) {
|
||||||
|
// Distinguish between an error that simply happened while "throw"
|
||||||
|
// was being evaluated and an explicit thrown error.
|
||||||
|
if (fn->name == "throw") {
|
||||||
|
addErrorTrace(e, pos, "caused by explicit %s", "throw");
|
||||||
|
} else {
|
||||||
|
addErrorTrace(e, pos, "while calling the '%s' builtin", fn->name);
|
||||||
|
}
|
||||||
|
throw;
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
|
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
|
||||||
throw;
|
throw;
|
||||||
|
|
|
@ -25,7 +25,7 @@ nix eval -E 'assert 1 + 2 == 3; true'
|
||||||
# Top-level eval errors should be printed to stderr with a traceback.
|
# Top-level eval errors should be printed to stderr with a traceback.
|
||||||
topLevelThrow="$(expectStderr 1 nix eval --expr 'throw "a sample throw message"')"
|
topLevelThrow="$(expectStderr 1 nix eval --expr 'throw "a sample throw message"')"
|
||||||
[[ "$topLevelThrow" =~ "a sample throw message" ]]
|
[[ "$topLevelThrow" =~ "a sample throw message" ]]
|
||||||
[[ "$topLevelThrow" =~ "while calling the 'throw' builtin" ]]
|
[[ "$topLevelThrow" =~ "caused by explicit throw" ]]
|
||||||
|
|
||||||
# But errors inside something should print an elided version, and exit with 0.
|
# But errors inside something should print an elided version, and exit with 0.
|
||||||
outputOfNestedThrow="$(nix eval --expr '{ throws = throw "a sample throw message"; }')"
|
outputOfNestedThrow="$(nix eval --expr '{ throws = throw "a sample throw message"; }')"
|
||||||
|
|
|
@ -41,7 +41,7 @@ error:
|
||||||
| ^
|
| ^
|
||||||
5| if n > 0
|
5| if n > 0
|
||||||
|
|
||||||
… while calling the 'throw' builtin
|
… caused by explicit throw
|
||||||
at /pwd/lang/eval-fail-duplicate-traces.nix:7:10:
|
at /pwd/lang/eval-fail-duplicate-traces.nix:7:10:
|
||||||
6| then throwAfter (n - 1)
|
6| then throwAfter (n - 1)
|
||||||
7| else throw "Uh oh!";
|
7| else throw "Uh oh!";
|
||||||
|
|
|
@ -27,7 +27,7 @@ error:
|
||||||
| ^
|
| ^
|
||||||
6|
|
6|
|
||||||
|
|
||||||
… while calling the 'throw' builtin
|
… caused by explicit throw
|
||||||
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:5:9:
|
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:5:9:
|
||||||
4| null
|
4| null
|
||||||
5| [ (_: throw "Not the final value, but is still forced!") (_: 23) ]
|
5| [ (_: throw "Not the final value, but is still forced!") (_: 23) ]
|
||||||
|
|
|
@ -54,7 +54,7 @@ error:
|
||||||
|
|
||||||
(21 duplicate frames omitted)
|
(21 duplicate frames omitted)
|
||||||
|
|
||||||
… while calling the 'throw' builtin
|
… caused by explicit throw
|
||||||
at /pwd/lang/eval-fail-mutual-recursion.nix:34:10:
|
at /pwd/lang/eval-fail-mutual-recursion.nix:34:10:
|
||||||
33| then throwAfterB true 10
|
33| then throwAfterB true 10
|
||||||
34| else throw "Uh oh!";
|
34| else throw "Uh oh!";
|
||||||
|
|
|
@ -5,7 +5,7 @@ error:
|
||||||
| ^
|
| ^
|
||||||
2|
|
2|
|
||||||
|
|
||||||
… while calling the 'throw' builtin
|
… caused by explicit throw
|
||||||
at /pwd/lang/eval-fail-not-throws.nix:1:4:
|
at /pwd/lang/eval-fail-not-throws.nix:1:4:
|
||||||
1| ! (throw "uh oh!")
|
1| ! (throw "uh oh!")
|
||||||
| ^
|
| ^
|
||||||
|
|
23
tests/functional/lang/eval-fail-select-err.err.exp
Normal file
23
tests/functional/lang/eval-fail-select-err.err.exp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
error:
|
||||||
|
… while evaluating the attribute 'src.meta'
|
||||||
|
at /pwd/lang/eval-fail-select-err.nix:2:3:
|
||||||
|
1| let
|
||||||
|
2| somepkg.src = throw "invalid foo bar";
|
||||||
|
| ^
|
||||||
|
3| in somepkg.src.meta
|
||||||
|
|
||||||
|
… while evaluating 'somepkg.src' to select 'meta' on it
|
||||||
|
at /pwd/lang/eval-fail-select-err.nix:3:4:
|
||||||
|
2| somepkg.src = throw "invalid foo bar";
|
||||||
|
3| in somepkg.src.meta
|
||||||
|
| ^
|
||||||
|
4|
|
||||||
|
|
||||||
|
… caused by explicit throw
|
||||||
|
at /pwd/lang/eval-fail-select-err.nix:2:17:
|
||||||
|
1| let
|
||||||
|
2| somepkg.src = throw "invalid foo bar";
|
||||||
|
| ^
|
||||||
|
3| in somepkg.src.meta
|
||||||
|
|
||||||
|
error: invalid foo bar
|
3
tests/functional/lang/eval-fail-select-err.nix
Normal file
3
tests/functional/lang/eval-fail-select-err.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
somepkg.src = throw "invalid foo bar";
|
||||||
|
in somepkg.src.meta
|
|
@ -40,7 +40,7 @@ error:
|
||||||
| ^
|
| ^
|
||||||
8| }
|
8| }
|
||||||
|
|
||||||
… while calling the 'throw' builtin
|
… caused by explicit throw
|
||||||
at /pwd/lang/eval-fail-toJSON.nix:7:13:
|
at /pwd/lang/eval-fail-toJSON.nix:7:13:
|
||||||
6| {
|
6| {
|
||||||
7| c.d = throw "hah no";
|
7| c.d = throw "hah no";
|
||||||
|
|
Loading…
Reference in a new issue