mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-12 07:35:07 +00:00
Ignore trailing quotes for unclosed l-brace errors (#9388)
## Summary Given: ```python F"{"ڤ ``` We try to locate the "unclosed left brace" error by subtracting the quote size from the lexer offset -- so we subtract 1 from the end of the source, which puts us in the middle of a Unicode character. I don't think we should try to adjust the offset in this way, since there can be content _after_ the quote. For example, with the advent of PEP 701, this string could reasonably be fixed as: ```python F"{"ڤ"}" ```` Closes https://github.com/astral-sh/ruff/issues/9379.
This commit is contained in:
parent
9a14f403c8
commit
f0d43dafcf
2 changed files with 35 additions and 21 deletions
|
@ -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,
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue