diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 1556004629..40c194d28f 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -2833,12 +2833,31 @@ pub fn parse_string(working_set: &mut StateWorkingSet, span: Span) -> Expression } // Check for unbalanced quotes: { - if bytes.starts_with(b"\"") && (bytes.len() == 1 || !bytes.ends_with(b"\"")) { - working_set.error(ParseError::Unclosed("\"".into(), span)); + if bytes.starts_with(b"\"") + && (bytes.into_iter().filter(|ch| **ch == b'"').count() > 1 && !bytes.ends_with(b"\"")) + { + let close_delimiter_index = bytes + .into_iter() + .skip(1) + .position(|ch| *ch == b'"') + .expect("Already check input bytes contains at least two double quotes"); + // needs `+2` rather than `+1`, because we have skip 1 to find close_delimiter_index before. + let span = Span::new(span.start + close_delimiter_index + 2, span.end); + working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(span)); return garbage(working_set, span); } - if bytes.starts_with(b"\'") && (bytes.len() == 1 || !bytes.ends_with(b"\'")) { - working_set.error(ParseError::Unclosed("\'".into(), span)); + + if bytes.starts_with(b"\'") + && (bytes.into_iter().filter(|ch| **ch == b'\'').count() > 1 && !bytes.ends_with(b"\'")) + { + let close_delimiter_index = bytes + .into_iter() + .skip(1) + .position(|ch| *ch == b'\'') + .expect("Already check input bytes contains at least two double quotes"); + // needs `+2` rather than `+1`, because we have skip 1 to find close_delimiter_index before. + let span = Span::new(span.start + close_delimiter_index + 2, span.end); + working_set.error(ParseError::ExtraTokensAfterClosingDelimiter(span)); return garbage(working_set, span); } } diff --git a/crates/nu-protocol/src/errors/parse_error.rs b/crates/nu-protocol/src/errors/parse_error.rs index 28c865b841..5254b0a83f 100644 --- a/crates/nu-protocol/src/errors/parse_error.rs +++ b/crates/nu-protocol/src/errors/parse_error.rs @@ -17,6 +17,13 @@ pub enum ParseError { #[diagnostic(code(nu::parser::extra_tokens), help("Try removing them."))] ExtraTokens(#[label = "extra tokens"] Span), + #[error("Invaild characters after closing delimiter")] + #[diagnostic( + code(nu::parser::extra_token_after_closing_delimiter), + help("Try removing them.") + )] + ExtraTokensAfterClosingDelimiter(#[label = "invalid characters"] Span), + #[error("Extra positional argument.")] #[diagnostic(code(nu::parser::extra_positional), help("Usage: {0}"))] ExtraPositional(String, #[label = "extra positional argument"] Span), @@ -577,6 +584,7 @@ impl ParseError { ParseError::LabeledErrorWithHelp { span: s, .. } => *s, ParseError::RedirectingBuiltinCommand(_, s, _) => *s, ParseError::UnexpectedSpreadArg(_, s) => *s, + ParseError::ExtraTokensAfterClosingDelimiter(s) => *s, } } } diff --git a/tests/repl/test_strings.rs b/tests/repl/test_strings.rs index f737c0e48f..1c573f03ae 100644 --- a/tests/repl/test_strings.rs +++ b/tests/repl/test_strings.rs @@ -38,8 +38,8 @@ fn non_string_in_record() -> TestResult { #[test] fn unbalance_string() -> TestResult { - fail_test(r#""aaaab"cc"#, "unclosed \"")?; - fail_test(r#"'aaaab'cc"#, "unclosed '") + fail_test(r#""aaaab"cc"#, "invalid characters")?; + fail_test(r#"'aaaab'cc"#, "invalid characters") } #[test]