mirror of
https://github.com/NixOS/nix.git
synced 2025-04-15 13:47:34 +00:00
builtins.catchEvalError
Co-authored-by: Alexander Foremny <aforemny@posteo.de>
This commit is contained in:
parent
f0b7b37425
commit
9061f04da5
@ -63,6 +63,91 @@ namespace nix {
|
||||
ASSERT_THAT(v, IsIntEq(1));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catchEvalErrorFailureThrow) {
|
||||
auto v = eval("builtins.catchEvalError (throw \"\")");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catchEvalErrorFailureAbort) {
|
||||
auto v = eval("builtins.catchEvalError (abort \"\")");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catchEvalErrorFailureMissingAttr) {
|
||||
auto v = eval("builtins.catchEvalError {}.a");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catchEvalErrorFailureImportFileNotFound) {
|
||||
auto v = eval("builtins.catchEvalError (import /does-not-exist)");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catchEvalErrorFailureReadFileFileNotFound) {
|
||||
auto v = eval("builtins.catchEvalError (builtins.readFile /does-not-exist)");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catchEvalErrorFailureFromJSON) {
|
||||
auto v = eval("builtins.catchEvalError (builtins.fromJSON \"{\")");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catchEvalErrorFailureInfRec) {
|
||||
auto v = eval("builtins.catchEvalError (let a = b; b = a; in a)");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catchEvalErrorFailureOutOfBounds) {
|
||||
auto v = eval("builtins.catchEvalError (builtins.head [])");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(1));
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsFalse());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, catchEvalErrorSuccess) {
|
||||
auto v = eval("builtins.catchEvalError 123");
|
||||
ASSERT_THAT(v, IsAttrs());
|
||||
auto s = createSymbol("success");
|
||||
auto p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsTrue());
|
||||
s = createSymbol("value");
|
||||
p = v.attrs()->get(s);
|
||||
ASSERT_NE(p, nullptr);
|
||||
ASSERT_THAT(*p->value, IsIntEq(123));
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, tryEvalFailure) {
|
||||
auto v = eval("builtins.tryEval (throw \"\")");
|
||||
ASSERT_THAT(v, IsAttrsOfSize(2));
|
||||
|
@ -981,6 +981,58 @@ static RegisterPrimOp primop_tryEval({
|
||||
.fun = prim_tryEval,
|
||||
});
|
||||
|
||||
/* Similar to `builtins.tryEval` but catches more, `value` attribute provided only on success. */
|
||||
static void prim_catchEvalError(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
auto attrs = state.buildBindings(2);
|
||||
|
||||
/* increment state.trylevel, and decrement it when this function returns. */
|
||||
MaintainCount trylevel(state.trylevel);
|
||||
|
||||
ReplExitStatus (* savedDebugRepl)(ref<EvalState> es, const ValMap & extraEnv) = nullptr;
|
||||
if (state.debugRepl && state.settings.ignoreExceptionsDuringTry)
|
||||
{
|
||||
/* to prevent starting the repl from exceptions withing a tryEval, null it. */
|
||||
savedDebugRepl = state.debugRepl;
|
||||
state.debugRepl = nullptr;
|
||||
}
|
||||
|
||||
try {
|
||||
state.forceValue(*args[0], pos);
|
||||
attrs.insert(state.sValue, args[0]);
|
||||
attrs.insert(state.symbols.create("success"), &state.vTrue);
|
||||
} catch (EvalError & e) {
|
||||
attrs.insert(state.symbols.create("success"), &state.vFalse);
|
||||
} catch (FileNotFound & e) {
|
||||
attrs.insert(state.symbols.create("success"), &state.vFalse);
|
||||
}
|
||||
|
||||
// restore the debugRepl pointer if we saved it earlier.
|
||||
if (savedDebugRepl)
|
||||
state.debugRepl = savedDebugRepl;
|
||||
|
||||
v.mkAttrs(attrs);
|
||||
}
|
||||
|
||||
static RegisterPrimOp primop_catchEvalError({
|
||||
.name = "__catchEvalError",
|
||||
.args = {"e"},
|
||||
.doc = R"(
|
||||
Similar to `builtins.tryEval` except that it catches
|
||||
- `throw`
|
||||
- `assert`
|
||||
- `abort`
|
||||
- missing attribute
|
||||
- out of bounds indexing
|
||||
- file not found such as `import` and `builtins.readFile`
|
||||
- deserialization such as `builtins.fromJSON`
|
||||
- detectable infinite recursion
|
||||
|
||||
And that `value` attribute exists only on success.
|
||||
)",
|
||||
.fun = prim_catchEvalError,
|
||||
});
|
||||
|
||||
/* Return an environment variable. Use with care. */
|
||||
static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user