diff --git a/Cargo.lock b/Cargo.lock index 6f02f8af51984d..334539728208a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2350,6 +2350,7 @@ name = "ruff_python_index" version = "0.0.0" dependencies = [ "itertools 0.11.0", + "log", "ruff_python_ast", "ruff_python_parser", "ruff_python_trivia", 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 14fff96dfb5dc1..8542a5ab168079 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(range) => (*a_range, range), + None => continue, + } + } + (Tok::FStringEnd, Tok::String { .. }) => { + match indexer.fstring_ranges().innermost(a_range.start()) { + Some(range) => (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 4a333bdcd52065..d2d0f71c11460a 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 @@ -50,18 +50,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, }; @@ -166,25 +169,14 @@ pub(crate) fn invalid_escape_sequence( ))); } } 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 diagnostic in &mut invalid_escape_sequence { // If necessary, add a space between any leading keyword (`return`, `yield`, // `assert`, etc.) and the string. For example, `return"foo"` is valid, but // `returnr"foo"` is not. diagnostic.set_fix(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, ))); } } diff --git a/crates/ruff_python_index/Cargo.toml b/crates/ruff_python_index/Cargo.toml index b82aaeeab94bce..fb6cc1bcb53855 100644 --- a/crates/ruff_python_index/Cargo.toml +++ b/crates/ruff_python_index/Cargo.toml @@ -21,5 +21,6 @@ ruff_source_file = { path = "../ruff_source_file" } ruff_text_size = { path = "../ruff_text_size" } itertools = { workspace = true } +log = { workspace = true } [dev-dependencies] diff --git a/crates/ruff_python_index/src/fstring_ranges.rs b/crates/ruff_python_index/src/fstring_ranges.rs index b95c9b665083ba..03408ea275504f 100644 --- a/crates/ruff_python_index/src/fstring_ranges.rs +++ b/crates/ruff_python_index/src/fstring_ranges.rs @@ -1,5 +1,7 @@ use std::collections::BTreeMap; +use log::debug; + use ruff_python_parser::Tok; use ruff_text_size::{TextRange, TextSize}; @@ -7,6 +9,7 @@ use ruff_text_size::{TextRange, TextSize}; /// There can be multiple overlapping ranges for nested f-strings. #[derive(Debug)] pub struct FStringRanges { + // Mapping from the f-string start location to its range. raw: BTreeMap, } @@ -89,7 +92,12 @@ impl FStringRangesBuilder { } pub(crate) fn finish(self) -> FStringRanges { - debug_assert!(self.start_locations.is_empty()); + if !self.start_locations.is_empty() { + debug!( + "Unterminated f-strings detected at: {:?}", + self.start_locations + ); + } FStringRanges { raw: self.raw } } }