This commit is contained in:
Shahar "Dawn" Or 2025-04-13 12:31:24 +00:00 committed by GitHub
commit cbbadbb507
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 137 additions and 0 deletions

View File

@ -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));

View File

@ -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)
{