From 0bb2e47c98c6727c05ebf98b5f9e6a31b5bb30ea Mon Sep 17 00:00:00 2001 From: Kangaxx-0 <85712372+Kangaxx-0@users.noreply.github.com> Date: Tue, 24 Jan 2023 00:05:46 -0800 Subject: [PATCH] Incorrect parsing of unbalanced braces based on issue 6914 (#7621) --- .../nu-command/tests/format_conversions/nuon.rs | 2 +- crates/nu-parser/src/errors.rs | 5 +++++ crates/nu-parser/src/lex.rs | 17 +++++++++++++++++ crates/nu-parser/src/parser.rs | 13 +++++++++---- src/tests/test_parser.rs | 5 +++++ tests/overlays/samples/spam.nu | 2 +- 6 files changed, 38 insertions(+), 6 deletions(-) diff --git a/crates/nu-command/tests/format_conversions/nuon.rs b/crates/nu-command/tests/format_conversions/nuon.rs index d011a975db..1898d73fa7 100644 --- a/crates/nu-command/tests/format_conversions/nuon.rs +++ b/crates/nu-command/tests/format_conversions/nuon.rs @@ -396,7 +396,7 @@ proptest! { {{"{0}": "sam"}} | to nuon | from nuon; "#, c).as_ref() )); - assert!(actual.err.is_empty() || actual.err.contains("Unexpected end of code") || actual.err.contains("only strings can be keys")); + assert!(actual.err.is_empty() || actual.err.contains("Unexpected end of code") || actual.err.contains("only strings can be keys") || actual.err.contains("unbalanced { and }")); // The second is for weird escapes due to backslashes // The third is for chars like '0' } diff --git a/crates/nu-parser/src/errors.rs b/crates/nu-parser/src/errors.rs index 89604f141c..e5020f9d8c 100644 --- a/crates/nu-parser/src/errors.rs +++ b/crates/nu-parser/src/errors.rs @@ -34,6 +34,10 @@ pub enum ParseError { #[diagnostic(code(nu::parser::unclosed_delimiter), url(docsrs))] Unclosed(String, #[label("unclosed {0}")] Span), + #[error("Unbalanced delimiter.")] + #[diagnostic(code(nu::parser::unbalanced_delimiter), url(docsrs))] + Unbalanced(String, String, #[label("unbalanced {0} and {1}")] Span), + #[error("Parse mismatch during operation.")] #[diagnostic(code(nu::parser::parse_mismatch), url(docsrs))] Expected(String, #[label("expected {0}")] Span), @@ -450,6 +454,7 @@ impl ParseError { ParseError::ExtraPositional(_, s) => *s, ParseError::UnexpectedEof(_, s) => *s, ParseError::Unclosed(_, s) => *s, + ParseError::Unbalanced(_, _, s) => *s, ParseError::Expected(_, s) => *s, ParseError::Mismatch(_, _, s) => *s, ParseError::UnsupportedOperation(_, _, _, s, _) => *s, diff --git a/crates/nu-parser/src/lex.rs b/crates/nu-parser/src/lex.rs index 8dce2f90aa..cbb94cfaec 100644 --- a/crates/nu-parser/src/lex.rs +++ b/crates/nu-parser/src/lex.rs @@ -170,6 +170,23 @@ pub fn lex_item( // We encountered a closing `}` delimiter. Pop off the opening `{`. if let Some(BlockKind::CurlyBracket) = block_level.last() { let _ = block_level.pop(); + } else { + // We encountered a closing `}` delimiter, but the last opening + // delimiter was not a `{`. This is an error. + let span = Span::new(span_offset + token_start, span_offset + *curr_offset); + + *curr_offset += 1; + return ( + Token { + contents: TokenContents::Item, + span, + }, + Some(ParseError::Unbalanced( + "{".to_string(), + "}".to_string(), + Span::new(span.end, span.end), + )), + ); } } else if c == b'(' { // We encountered an opening `(` delimiter. diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 776853c6c9..07b5a9d3a0 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -4392,10 +4392,15 @@ pub fn parse_value( } b'{' => { if !matches!(shape, SyntaxShape::Closure(..)) && !matches!(shape, SyntaxShape::Block) { - if let (expr, None) = - parse_full_cell_path(working_set, None, span, expand_aliases_denylist) - { - return (expr, None); + let (expr, err) = + parse_full_cell_path(working_set, None, span, expand_aliases_denylist); + match err { + Some(err) => { + if let ParseError::Unbalanced(_, _, _) = err { + return (expr, Some(err)); + } + } + None => return (expr, None), } } if matches!(shape, SyntaxShape::Closure(_)) || matches!(shape, SyntaxShape::Any) { diff --git a/src/tests/test_parser.rs b/src/tests/test_parser.rs index 7657715005..e054c1a7b0 100644 --- a/src/tests/test_parser.rs +++ b/src/tests/test_parser.rs @@ -470,3 +470,8 @@ fn or_and_xor() -> TestResult { // Assumes the precedence NOT > AND > XOR > OR run_test(r#"true or false xor true or false"#, "true") } + +#[test] +fn unbalanced_delimiter() -> TestResult { + fail_test(r#"{a:{b:5}}}"#, "unbalanced { and }") +} diff --git a/tests/overlays/samples/spam.nu b/tests/overlays/samples/spam.nu index 1384df5407..e250e004e3 100644 --- a/tests/overlays/samples/spam.nu +++ b/tests/overlays/samples/spam.nu @@ -2,4 +2,4 @@ export def foo [] { "foo" } export alias bar = "bar" -export-env { let-env BAZ = "baz" } } +export-env { let-env BAZ = "baz" }