From 8420d1c7449eaee281f83a9a02c2553f28ccf76d Mon Sep 17 00:00:00 2001 From: Akash h g Date: Sat, 26 Sep 2026 15:31:26 +0530 Subject: [PATCH] repl: fix multi-line attribute set in variable binding (issue #16310) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a user typed  = { followed by Enter in nix repl, the REPL produced a confusing error: error: syntax error, unexpected '=', expecting end of file at «string»:1:3: 1| a = { | ^ The root cause was in NixRepl::parseReplBindings: the first parse attempt (without semicolon) caught *all* ParseErrors silently. When the input was an incomplete attrset binding (e.g.  = {), the parse fails with 'unexpected end of file' — an incomplete-input signal. Because this was swallowed, the code fell through to the semicolon-retry, which produced a different syntax error (not 'unexpected end of file'), so the second catch returned ullptr. Back in processLine, ullptr caused the line to be evaluated as a Nix expression, which correctly rejected  = { as invalid expression syntax. Fix: mirror the isIncompleteInput check already present in the second catch block into the first catch block, throwing IncompleteReplExpr so the main loop prompts for continuation. This is analogous to the fix for multi-line strings in bindings (issue #15801, PR #15892). The single-line case  = { b = 1; } is unaffected — only incomplete inputs (missing closing }) trigger the continuation prompt. Add two characterisation tests: - multiline-attrset-binding: variable bound to a multi-line attrset - multiline-attrset-expr: multi-line attrset expression (regression guard) --- src/libcmd/repl.cc | 8 +++++++- .../suites/repl/cases/multiline-attrset-binding.expected | 9 +++++++++ .../suites/repl/cases/multiline-attrset-binding.in | 5 +++++ .../suites/repl/cases/multiline-attrset-expr.expected | 7 +++++++ .../suites/repl/cases/multiline-attrset-expr.in | 4 ++++ 5 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/functional/suites/repl/cases/multiline-attrset-binding.expected create mode 100644 tests/functional/suites/repl/cases/multiline-attrset-binding.in create mode 100644 tests/functional/suites/repl/cases/multiline-attrset-expr.expected create mode 100644 tests/functional/suites/repl/cases/multiline-attrset-expr.in diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 9e3d99d75d13..d33ac8ac76d2 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -902,7 +902,13 @@ ExprAttrs * NixRepl::parseReplBindings(std::string s) // Try parsing as bindings try { return state->parseReplBindings(s, basePath, staticEnv); - } catch (ParseError &) { + } catch (ParseError & e) { + // If the input is incomplete (e.g. `a = {` without closing `}`), + // signal the main loop to prompt for continuation instead of falling + // through to expression evaluation (which would produce a confusing + // "unexpected '='" error). + if (isIncompleteInput(e)) + throw IncompleteReplExpr(e.msg()); } // Try with semicolon appended (for `inherit foo` shorthand) diff --git a/tests/functional/suites/repl/cases/multiline-attrset-binding.expected b/tests/functional/suites/repl/cases/multiline-attrset-binding.expected new file mode 100644 index 000000000000..52542b688894 --- /dev/null +++ b/tests/functional/suites/repl/cases/multiline-attrset-binding.expected @@ -0,0 +1,9 @@ +Nix +Type :? for help. + +nix-repl> a = { + > b = 1; + > } + +nix-repl> a +{ b = 1; } diff --git a/tests/functional/suites/repl/cases/multiline-attrset-binding.in b/tests/functional/suites/repl/cases/multiline-attrset-binding.in new file mode 100644 index 000000000000..742f7cdec687 --- /dev/null +++ b/tests/functional/suites/repl/cases/multiline-attrset-binding.in @@ -0,0 +1,5 @@ +# COM: Multi-line attribute set in binding should trigger continuation (issue #16310) +a = { + b = 1; +} +a diff --git a/tests/functional/suites/repl/cases/multiline-attrset-expr.expected b/tests/functional/suites/repl/cases/multiline-attrset-expr.expected new file mode 100644 index 000000000000..e644eddd32a3 --- /dev/null +++ b/tests/functional/suites/repl/cases/multiline-attrset-expr.expected @@ -0,0 +1,7 @@ +Nix +Type :? for help. + +nix-repl> { + > x = 1; + > } +{ x = 1; } diff --git a/tests/functional/suites/repl/cases/multiline-attrset-expr.in b/tests/functional/suites/repl/cases/multiline-attrset-expr.in new file mode 100644 index 000000000000..57c538a6fb61 --- /dev/null +++ b/tests/functional/suites/repl/cases/multiline-attrset-expr.in @@ -0,0 +1,4 @@ +# COM: Multi-line attribute set expression (not binding) should still trigger continuation (issue #16310) +{ + x = 1; +}