diff --git a/crates/ruff_python_parser/src/lexer.rs b/crates/ruff_python_parser/src/lexer.rs index eb2a1f73f1..154a9a26bf 100644 --- a/crates/ruff_python_parser/src/lexer.rs +++ b/crates/ruff_python_parser/src/lexer.rs @@ -700,7 +700,7 @@ impl<'source> Lexer<'source> { } Some('\r' | '\n') if !triple_quoted => { if let Some(fstring) = self.fstrings.current() { - // When we are in an f-string, check whether does the initial quote + // When we are in an f-string, check whether the initial quote // matches with f-strings quotes and if it is, then this must be a // missing '}' token so raise the proper error. if fstring.quote_char() == quote && !fstring.is_triple_quoted() { @@ -708,7 +708,7 @@ impl<'source> Lexer<'source> { error: LexicalErrorType::FStringError( FStringErrorType::UnclosedLbrace, ), - location: self.offset() - fstring.quote_size(), + location: self.offset() - TextSize::new(1), }); } } @@ -732,7 +732,7 @@ impl<'source> Lexer<'source> { Some(_) => {} None => { if let Some(fstring) = self.fstrings.current() { - // When we are in an f-string, check whether does the initial quote + // When we are in an f-string, check whether the initial quote // matches with f-strings quotes and if it is, then this must be a // missing '}' token so raise the proper error. if fstring.quote_char() == quote @@ -742,7 +742,7 @@ impl<'source> Lexer<'source> { error: LexicalErrorType::FStringError( FStringErrorType::UnclosedLbrace, ), - location: self.offset() - fstring.quote_size(), + location: self.offset(), }); } } @@ -2195,13 +2195,17 @@ f"{(lambda x:{x})}" assert_debug_snapshot!(lex_jupyter_source(source)); } + fn lex_error(source: &str) -> LexicalError { + match lex(source, Mode::Module).find_map(Result::err) { + Some(err) => err, + _ => panic!("Expected at least one error"), + } + } + fn lex_fstring_error(source: &str) -> FStringErrorType { - match lex(source, Mode::Module).find_map(std::result::Result::err) { - Some(err) => match err.error { - LexicalErrorType::FStringError(error) => error, - _ => panic!("Expected FStringError: {err:?}"), - }, - _ => panic!("Expected atleast one FStringError"), + match lex_error(source).error { + LexicalErrorType::FStringError(error) => error, + err => panic!("Expected FStringError: {err:?}"), } } @@ -2246,4 +2250,25 @@ f"{(lambda x:{x})}" UnterminatedTripleQuotedString ); } + + #[test] + fn test_fstring_error_location() { + assert_debug_snapshot!(lex_error("f'{'"), @r###" + LexicalError { + error: FStringError( + UnclosedLbrace, + ), + location: 4, + } + "###); + + assert_debug_snapshot!(lex_error("f'{'α"), @r###" + LexicalError { + error: FStringError( + UnclosedLbrace, + ), + location: 6, + } + "###); + } } diff --git a/crates/ruff_python_parser/src/lexer/fstring.rs b/crates/ruff_python_parser/src/lexer/fstring.rs index 8acf00d52c..83a2965e43 100644 --- a/crates/ruff_python_parser/src/lexer/fstring.rs +++ b/crates/ruff_python_parser/src/lexer/fstring.rs @@ -1,7 +1,5 @@ use bitflags::bitflags; -use ruff_text_size::TextSize; - bitflags! { #[derive(Debug)] pub(crate) struct FStringContextFlags: u8 { @@ -58,15 +56,6 @@ impl FStringContext { } } - /// Returns the number of quotes for the current f-string. - pub(crate) const fn quote_size(&self) -> TextSize { - if self.is_triple_quoted() { - TextSize::new(3) - } else { - TextSize::new(1) - } - } - /// Returns the triple quotes for the current f-string if it is a triple-quoted /// f-string, `None` otherwise. pub(crate) const fn triple_quotes(&self) -> Option<&'static str> {