diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 6eb87aadc2..418f0dca70 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2004,7 +2004,11 @@ pub fn parse_paren_expr( if fcp_error_count > starting_error_count { let malformed_subexpr = working_set.parse_errors[starting_error_count..] .first() - .is_some_and(|e| matches!(e, ParseError::Unclosed(right, _) if right == ")" )); + .is_some_and(|e| match e { + ParseError::Unclosed(right, _) if (right == ")") => true, + ParseError::Unbalanced(left, right, _) if left == "(" && right == ")" => true, + _ => false, + }); if malformed_subexpr { working_set.parse_errors.truncate(starting_error_count); parse_string_interpolation(working_set, span) diff --git a/crates/nu-parser/tests/test_parser.rs b/crates/nu-parser/tests/test_parser.rs index be6c515987..2267916322 100644 --- a/crates/nu-parser/tests/test_parser.rs +++ b/crates/nu-parser/tests/test_parser.rs @@ -1636,6 +1636,46 @@ mod string { assert_eq!(subexprs[3], &Expr::String("bar".to_string())); } + /// PR with summary of the issue: https://github.com/nushell/nushell/pull/16235 + /// Release Notes Mention: https://www.nushell.sh/blog/2025-07-23-nushell_0_106_0.html#regression-bare-word-interpolation-on-both-sides-does-not-work-toc + #[test] + pub fn parse_string_interpolation_bare_starting_and_ending_subexpr() { + let engine_state = EngineState::new(); + let mut working_set = StateWorkingSet::new(&engine_state); + + let block = parse( + &mut working_set, + None, + b"(100 + 20 + 3)/bar/(300 + 20 + 1)", + true, + ); + + assert!(working_set.parse_errors.is_empty(),); + + let [pipeline] = block.pipelines.as_slice() else { + panic!("expected 1 pipeline") + }; + let [element] = pipeline.elements.as_slice() else { + panic!("expected 1 pipeline element") + }; + assert!(element.redirection.is_none()); + + let Expr::StringInterpolation(expressions) = &element.expr.expr else { + panic!("Expected an `Expr::StringInterpolation`") + }; + let subexprs: Vec<_> = expressions.iter().map(|e| &e.expr).collect(); + + let [ + Expr::FullCellPath(..), + Expr::String(s), + Expr::FullCellPath(..), + ] = subexprs.as_slice() + else { + panic!("AST does not have the expected structure") + }; + assert_eq!(s, "/bar/"); + } + #[test] pub fn parse_string_interpolation_bare_starting_subexpr_external_arg() { let engine_state = EngineState::new();