libexpr: add builtins.warn

Added builtins.warn` which takes two arguments: a message that is
displayed as a warning during evaluation which must be a string and a
value that is returned from the expression.

The next commits add new settings to control the behavior of the new
builtin: `debugger-on-warn` allows the user to start the debugger and
`abort-on-warn` aborts evaluation with an error.

Unlike upstream, I chose not to mark evaluation warnings from
`builtins.warn` as distinct from other warnings because that breaks the
commonly expected logging format `level: message`.

Co-authored-by: Qyriad <qyriad@qyriad.me>
Upstream-PR: https://github.com/NixOS/nix/pull/10592
Fixes: https://git.lix.systems/lix-project/lix/issues/579
Change-Id: I8658c88e5c27952b65e8b9f5525a572e0680cc1f
This commit is contained in:
Emilia Bopp
2025-11-20 17:52:29 +01:00
committed by Qyriad
co-authored by Qyriad
parent dd3ba39384
commit 0ade82d23a
12 changed files with 64 additions and 0 deletions
+5
View File
@@ -176,6 +176,11 @@ midnightveil:
forgejo: midnightveil
github: midnightveil
milibopp:
display_name: Emilia Bopp
forgejo: milibopp
github: milibopp
nan-git:
display_name: NaN-git
github: NaN-git
+11
View File
@@ -0,0 +1,11 @@
---
synopsis: "Add `builtins.warn` for emitting warnings from Nix code"
cls: [2248]
category: "Features"
credits: [milibopp, Qyriad]
---
Lix now has a builtin function for emitting warnings.
Like `builtins.trace`, it takes two arguments: the message to emit, and the expression to return.
_Unlike_ `builtins.trace`, `builtins.warn` requires the first argument — the message — to be a string.
In the future we may extend `builtins.warn` to accept a more structured API.
+6
View File
@@ -0,0 +1,6 @@
---
name: warn
args: [msg, e2]
---
Evaluate string *msg* and print it on standard error. Then return *e2*.
This function is useful for warning about unexpected conditions without aborting evaluation.
+1
View File
@@ -148,6 +148,7 @@ builtin_definitions = files(
'builtins/typeOf.md',
'builtins/unsafeDiscardOutputDependency.md',
'builtins/unsafeGetAttrPos.md',
'builtins/warn.md',
'builtins/zipAttrsWith.md',
# keep-sorted end
)
+13
View File
@@ -768,6 +768,19 @@ static void prim_trace(EvalState & state, Value * * args, Value & v)
v = *args[1];
}
static void prim_warn(EvalState & state, Value ** args, Value & v)
{
// We only accept a string argument for now. The use case for pretty printing a value is covered
// by `trace`. By rejecting non-strings we allow future versions to add more features without
// breaking existing code.
auto const msg =
state.forceString(*args[0], noPos, "while evaluating message for builtins.warn");
printTaggedWarning("%s", Uncolored(msg));
state.forceValue(*args[1], noPos);
v = *args[1];
}
/* Takes two arguments and evaluates to the second one. Used as the
* builtins.traceVerbose implementation when --trace-verbose is not enabled
@@ -0,0 +1,10 @@
error:
… while calling the 'warn' builtin
at /pwd/in.nix:1:1:
1| builtins.warn { cat = "girl"; } 1337
| ^
2|
… while evaluating message for builtins.warn
error: expected a string but found a set: { cat = "girl"; }
@@ -0,0 +1 @@
warning: meow
@@ -0,0 +1 @@
1337
@@ -0,0 +1 @@
builtins.warn { cat = "girl"; } 1337
@@ -0,0 +1 @@
builtins.warn "meow" 1337
@@ -0,0 +1,6 @@
[[test]]
runner = "eval-okay"
[[test]]
runner = "eval-fail"
in = "in-reject-non-string.nix"
+8
View File
@@ -121,6 +121,14 @@ namespace nix {
ASSERT_NE(text.find("test string 123"), std::string::npos);
}
TEST_F(PrimOpTest, warn) {
CaptureLogging l;
auto const v = eval("builtins.warn \"test string 123\" 123");
ASSERT_THAT(v, IsIntEq(123));
auto const text = l.get();
ASSERT_NE(text.find("test string 123"), std::string::npos);
}
TEST_F(PrimOpTest, placeholder) {
auto v = eval("builtins.placeholder \"out\"");
ASSERT_THAT(v, IsStringEq("/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"));