aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQyriad <qyriad@qyriad.me>2024-06-24 17:26:21 -0600
committerQyriad <qyriad@qyriad.me>2024-07-04 17:43:03 -0600
commit4c7165be86e32cce4ca7eca59bd0ec2130bbb360 (patch)
tree9c43030bdb545d54687cd31ea62c4bcb7a84ad17
parent14bf54bd393f1b48ba519d104abd53434fc18e75 (diff)
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
-rw-r--r--doc/manual/rl-next/distinguish-throw-errors.md70
-rw-r--r--src/libexpr/eval.cc9
-rw-r--r--tests/functional/eval.sh2
-rw-r--r--tests/functional/lang/eval-fail-duplicate-traces.err.exp2
-rw-r--r--tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp2
-rw-r--r--tests/functional/lang/eval-fail-mutual-recursion.err.exp2
-rw-r--r--tests/functional/lang/eval-fail-not-throws.err.exp2
-rw-r--r--tests/functional/lang/eval-fail-select-err.err.exp23
-rw-r--r--tests/functional/lang/eval-fail-select-err.nix3
-rw-r--r--tests/functional/lang/eval-fail-toJSON.err.exp2
10 files changed, 111 insertions, 6 deletions
diff --git a/doc/manual/rl-next/distinguish-throw-errors.md b/doc/manual/rl-next/distinguish-throw-errors.md
new file mode 100644
index 000000000..243e33d61
--- /dev/null
+++ b/doc/manual/rl-next/distinguish-throw-errors.md
@@ -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`.
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 56581cc19..d9bdb0d2c 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -1807,6 +1807,15 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
try {
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) {
addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name);
throw;
diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh
index 9c125b569..ae6fcec63 100644
--- a/tests/functional/eval.sh
+++ b/tests/functional/eval.sh
@@ -25,7 +25,7 @@ nix eval -E 'assert 1 + 2 == 3; true'
# Top-level eval errors should be printed to stderr with a traceback.
topLevelThrow="$(expectStderr 1 nix eval --expr 'throw "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.
outputOfNestedThrow="$(nix eval --expr '{ throws = throw "a sample throw message"; }')"
diff --git a/tests/functional/lang/eval-fail-duplicate-traces.err.exp b/tests/functional/lang/eval-fail-duplicate-traces.err.exp
index cedaebd3b..d9e2ec945 100644
--- a/tests/functional/lang/eval-fail-duplicate-traces.err.exp
+++ b/tests/functional/lang/eval-fail-duplicate-traces.err.exp
@@ -41,7 +41,7 @@ error:
| ^
5| if n > 0
- … while calling the 'throw' builtin
+ … caused by explicit throw
at /pwd/lang/eval-fail-duplicate-traces.nix:7:10:
6| then throwAfter (n - 1)
7| else throw "Uh oh!";
diff --git a/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp
index 4903bc82d..6955fad13 100644
--- a/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp
+++ b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp
@@ -27,7 +27,7 @@ error:
| ^
6|
- … while calling the 'throw' builtin
+ … caused by explicit throw
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:5:9:
4| null
5| [ (_: throw "Not the final value, but is still forced!") (_: 23) ]
diff --git a/tests/functional/lang/eval-fail-mutual-recursion.err.exp b/tests/functional/lang/eval-fail-mutual-recursion.err.exp
index c034afcd5..c03d2e840 100644
--- a/tests/functional/lang/eval-fail-mutual-recursion.err.exp
+++ b/tests/functional/lang/eval-fail-mutual-recursion.err.exp
@@ -54,7 +54,7 @@ error:
(21 duplicate frames omitted)
- … while calling the 'throw' builtin
+ … caused by explicit throw
at /pwd/lang/eval-fail-mutual-recursion.nix:34:10:
33| then throwAfterB true 10
34| else throw "Uh oh!";
diff --git a/tests/functional/lang/eval-fail-not-throws.err.exp b/tests/functional/lang/eval-fail-not-throws.err.exp
index fc81f7277..5882a260a 100644
--- a/tests/functional/lang/eval-fail-not-throws.err.exp
+++ b/tests/functional/lang/eval-fail-not-throws.err.exp
@@ -5,7 +5,7 @@ error:
| ^
2|
- … while calling the 'throw' builtin
+ … caused by explicit throw
at /pwd/lang/eval-fail-not-throws.nix:1:4:
1| ! (throw "uh oh!")
| ^
diff --git a/tests/functional/lang/eval-fail-select-err.err.exp b/tests/functional/lang/eval-fail-select-err.err.exp
new file mode 100644
index 000000000..70ee50737
--- /dev/null
+++ b/tests/functional/lang/eval-fail-select-err.err.exp
@@ -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
diff --git a/tests/functional/lang/eval-fail-select-err.nix b/tests/functional/lang/eval-fail-select-err.nix
new file mode 100644
index 000000000..a7add0077
--- /dev/null
+++ b/tests/functional/lang/eval-fail-select-err.nix
@@ -0,0 +1,3 @@
+let
+ somepkg.src = throw "invalid foo bar";
+in somepkg.src.meta
diff --git a/tests/functional/lang/eval-fail-toJSON.err.exp b/tests/functional/lang/eval-fail-toJSON.err.exp
index ad267711b..18c334923 100644
--- a/tests/functional/lang/eval-fail-toJSON.err.exp
+++ b/tests/functional/lang/eval-fail-toJSON.err.exp
@@ -40,7 +40,7 @@ error:
| ^
8| }
- … while calling the 'throw' builtin
+ … caused by explicit throw
at /pwd/lang/eval-fail-toJSON.nix:7:13:
6| {
7| c.d = throw "hah no";