diff --git a/crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/implicit.rs b/crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/implicit.rs index 14fff96dfb..5e91f0ccea 100644 --- a/crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/implicit.rs +++ b/crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/implicit.rs @@ -110,18 +110,27 @@ pub(crate) fn implicit( { let (a_range, b_range) = match (a_tok, b_tok) { (Tok::String { .. }, Tok::String { .. }) => (*a_range, *b_range), - (Tok::String { .. }, Tok::FStringStart) => ( - *a_range, - indexer.fstring_ranges().innermost(b_range.start()).unwrap(), - ), - (Tok::FStringEnd, Tok::String { .. }) => ( - indexer.fstring_ranges().innermost(a_range.start()).unwrap(), - *b_range, - ), - (Tok::FStringEnd, Tok::FStringStart) => ( - indexer.fstring_ranges().innermost(a_range.start()).unwrap(), - indexer.fstring_ranges().innermost(b_range.start()).unwrap(), - ), + (Tok::String { .. }, Tok::FStringStart) => { + match indexer.fstring_ranges().innermost(b_range.start()) { + Some(b_range) => (*a_range, b_range), + None => continue, + } + } + (Tok::FStringEnd, Tok::String { .. }) => { + match indexer.fstring_ranges().innermost(a_range.start()) { + Some(a_range) => (a_range, *b_range), + None => continue, + } + } + (Tok::FStringEnd, Tok::FStringStart) => { + match ( + indexer.fstring_ranges().innermost(a_range.start()), + indexer.fstring_ranges().innermost(b_range.start()), + ) { + (Some(a_range), Some(b_range)) => (a_range, b_range), + _ => continue, + } + } _ => continue, }; diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/invalid_escape_sequence.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/invalid_escape_sequence.rs index 54fe956d55..b4d100aa9d 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/invalid_escape_sequence.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/invalid_escape_sequence.rs @@ -78,18 +78,21 @@ pub(crate) fn invalid_escape_sequence( token: &Tok, token_range: TextRange, ) { - let token_source_code = match token { + let (token_source_code, string_start_location) = match token { Tok::FStringMiddle { value, is_raw } => { if *is_raw { return; } - value.as_str() + let Some(range) = indexer.fstring_ranges().innermost(token_range.start()) else { + return; + }; + (value.as_str(), range.start()) } Tok::String { kind, .. } => { if kind.is_raw() { return; } - locator.slice(token_range) + (locator.slice(token_range), token_range.start()) } _ => return, }; @@ -206,17 +209,6 @@ pub(crate) fn invalid_escape_sequence( invalid_escape_sequence.push(diagnostic); } } else { - let tok_start = if token.is_f_string_middle() { - // SAFETY: If this is a `FStringMiddle` token, then the indexer - // must have the f-string range. - indexer - .fstring_ranges() - .innermost(token_range.start()) - .unwrap() - .start() - } else { - token_range.start() - }; // Turn into raw string. for invalid_escape_char in &invalid_escape_chars { let diagnostic = Diagnostic::new( @@ -231,8 +223,8 @@ pub(crate) fn invalid_escape_sequence( // `assert`, etc.) and the string. For example, `return"foo"` is valid, but // `returnr"foo"` is not. Fix::safe_edit(Edit::insertion( - pad_start("r".to_string(), tok_start, locator), - tok_start, + pad_start("r".to_string(), string_start_location, locator), + string_start_location, )), ); invalid_escape_sequence.push(diagnostic); diff --git a/crates/ruff_python_index/src/fstring_ranges.rs b/crates/ruff_python_index/src/fstring_ranges.rs index b95c9b6650..bdc31258bb 100644 --- a/crates/ruff_python_index/src/fstring_ranges.rs +++ b/crates/ruff_python_index/src/fstring_ranges.rs @@ -5,8 +5,11 @@ use ruff_text_size::{TextRange, TextSize}; /// Stores the ranges of all f-strings in a file sorted by [`TextRange::start`]. /// There can be multiple overlapping ranges for nested f-strings. +/// +/// Note that the ranges for all unterminated f-strings are not stored. #[derive(Debug)] pub struct FStringRanges { + // Mapping from the f-string start location to its range. raw: BTreeMap, } @@ -89,7 +92,6 @@ impl FStringRangesBuilder { } pub(crate) fn finish(self) -> FStringRanges { - debug_assert!(self.start_locations.is_empty()); FStringRanges { raw: self.raw } } }