Skip to content

Commit 7880903

Browse files
committed
Allow unterminated f-strings in the indexer
1 parent 7f4ea66 commit 7880903

File tree

5 files changed

+40
-29
lines changed

5 files changed

+40
-29
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ruff_linter/src/rules/flake8_implicit_str_concat/rules/implicit.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,27 @@ pub(crate) fn implicit(
110110
{
111111
let (a_range, b_range) = match (a_tok, b_tok) {
112112
(Tok::String { .. }, Tok::String { .. }) => (*a_range, *b_range),
113-
(Tok::String { .. }, Tok::FStringStart) => (
114-
*a_range,
115-
indexer.fstring_ranges().innermost(b_range.start()).unwrap(),
116-
),
117-
(Tok::FStringEnd, Tok::String { .. }) => (
118-
indexer.fstring_ranges().innermost(a_range.start()).unwrap(),
119-
*b_range,
120-
),
121-
(Tok::FStringEnd, Tok::FStringStart) => (
122-
indexer.fstring_ranges().innermost(a_range.start()).unwrap(),
123-
indexer.fstring_ranges().innermost(b_range.start()).unwrap(),
124-
),
113+
(Tok::String { .. }, Tok::FStringStart) => {
114+
match indexer.fstring_ranges().innermost(b_range.start()) {
115+
Some(range) => (*a_range, range),
116+
None => continue,
117+
}
118+
}
119+
(Tok::FStringEnd, Tok::String { .. }) => {
120+
match indexer.fstring_ranges().innermost(a_range.start()) {
121+
Some(range) => (range, *b_range),
122+
None => continue,
123+
}
124+
}
125+
(Tok::FStringEnd, Tok::FStringStart) => {
126+
match (
127+
indexer.fstring_ranges().innermost(a_range.start()),
128+
indexer.fstring_ranges().innermost(b_range.start()),
129+
) {
130+
(Some(a_range), Some(b_range)) => (a_range, b_range),
131+
_ => continue,
132+
}
133+
}
125134
_ => continue,
126135
};
127136

crates/ruff_linter/src/rules/pycodestyle/rules/invalid_escape_sequence.rs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,21 @@ pub(crate) fn invalid_escape_sequence(
5050
token: &Tok,
5151
token_range: TextRange,
5252
) {
53-
let token_source_code = match token {
53+
let (token_source_code, string_start_location) = match token {
5454
Tok::FStringMiddle { value, is_raw } => {
5555
if *is_raw {
5656
return;
5757
}
58-
value.as_str()
58+
let Some(range) = indexer.fstring_ranges().innermost(token_range.start()) else {
59+
return;
60+
};
61+
(value.as_str(), range.start())
5962
}
6063
Tok::String { kind, .. } => {
6164
if kind.is_raw() {
6265
return;
6366
}
64-
locator.slice(token_range)
67+
(locator.slice(token_range), token_range.start())
6568
}
6669
_ => return,
6770
};
@@ -166,25 +169,14 @@ pub(crate) fn invalid_escape_sequence(
166169
)));
167170
}
168171
} else {
169-
let tok_start = if token.is_f_string_middle() {
170-
// SAFETY: If this is a `FStringMiddle` token, then the indexer
171-
// must have the f-string range.
172-
indexer
173-
.fstring_ranges()
174-
.innermost(token_range.start())
175-
.unwrap()
176-
.start()
177-
} else {
178-
token_range.start()
179-
};
180172
// Turn into raw string.
181173
for diagnostic in &mut invalid_escape_sequence {
182174
// If necessary, add a space between any leading keyword (`return`, `yield`,
183175
// `assert`, etc.) and the string. For example, `return"foo"` is valid, but
184176
// `returnr"foo"` is not.
185177
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
186-
pad_start("r".to_string(), tok_start, locator),
187-
tok_start,
178+
pad_start("r".to_string(), string_start_location, locator),
179+
string_start_location,
188180
)));
189181
}
190182
}

crates/ruff_python_index/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ ruff_source_file = { path = "../ruff_source_file" }
2121
ruff_text_size = { path = "../ruff_text_size" }
2222

2323
itertools = { workspace = true }
24+
log = { workspace = true }
2425

2526
[dev-dependencies]

crates/ruff_python_index/src/fstring_ranges.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use std::collections::BTreeMap;
22

3+
use log::debug;
4+
35
use ruff_python_parser::Tok;
46
use ruff_text_size::{TextRange, TextSize};
57

68
/// Stores the ranges of all f-strings in a file sorted by [`TextRange::start`].
79
/// There can be multiple overlapping ranges for nested f-strings.
810
#[derive(Debug)]
911
pub struct FStringRanges {
12+
// Mapping from the f-string start location to its range.
1013
raw: BTreeMap<TextSize, TextRange>,
1114
}
1215

@@ -89,7 +92,12 @@ impl FStringRangesBuilder {
8992
}
9093

9194
pub(crate) fn finish(self) -> FStringRanges {
92-
debug_assert!(self.start_locations.is_empty());
95+
if !self.start_locations.is_empty() {
96+
debug!(
97+
"Unterminated f-strings detected at: {:?}",
98+
self.start_locations
99+
);
100+
}
93101
FStringRanges { raw: self.raw }
94102
}
95103
}

0 commit comments

Comments
 (0)