Skip to content

Commit 4ffd4ed

Browse files
authored
Correct quick fix message for W605 (#8255)
## Summary This PR fixes the `W605` rule implementation to provide the quickfix message as per the fix provided. ## Test Plan Update snapshots. fixes: #8155
1 parent a4dd1e5 commit 4ffd4ed

4 files changed

+88
-38
lines changed

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

+68-18
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
55
use ruff_python_index::Indexer;
66
use ruff_python_parser::Tok;
77
use ruff_source_file::Locator;
8-
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
8+
use ruff_text_size::{TextLen, TextRange, TextSize};
99

1010
use crate::fix::edits::pad_start;
1111

@@ -25,23 +25,51 @@ use crate::fix::edits::pad_start;
2525
/// regex = r"\.png$"
2626
/// ```
2727
///
28+
/// Or, if the string already contains a valid escape sequence:
29+
/// ```python
30+
/// value = "new line\nand invalid escape \_ here"
31+
/// ```
32+
///
33+
/// Use instead:
34+
/// ```python
35+
/// value = "new line\nand invalid escape \\_ here"
36+
/// ```
37+
///
2838
/// ## References
2939
/// - [Python documentation: String and Bytes literals](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals)
3040
#[violation]
31-
pub struct InvalidEscapeSequence(char);
41+
pub struct InvalidEscapeSequence {
42+
ch: char,
43+
fix_title: FixTitle,
44+
}
3245

3346
impl AlwaysFixableViolation for InvalidEscapeSequence {
3447
#[derive_message_formats]
3548
fn message(&self) -> String {
36-
let InvalidEscapeSequence(char) = self;
37-
format!("Invalid escape sequence: `\\{char}`")
49+
let InvalidEscapeSequence { ch, .. } = self;
50+
format!("Invalid escape sequence: `\\{ch}`")
3851
}
3952

4053
fn fix_title(&self) -> String {
41-
"Add backslash to escape sequence".to_string()
54+
match self.fix_title {
55+
FixTitle::AddBackslash => format!("Add backslash to escape sequence"),
56+
FixTitle::UseRawStringLiteral => format!("Use a raw string literal"),
57+
}
4258
}
4359
}
4460

61+
#[derive(Debug, PartialEq, Eq)]
62+
enum FixTitle {
63+
AddBackslash,
64+
UseRawStringLiteral,
65+
}
66+
67+
#[derive(Debug)]
68+
struct InvalidEscapeChar {
69+
ch: char,
70+
range: TextRange,
71+
}
72+
4573
/// W605
4674
pub(crate) fn invalid_escape_sequence(
4775
diagnostics: &mut Vec<Diagnostic>,
@@ -67,7 +95,7 @@ pub(crate) fn invalid_escape_sequence(
6795
};
6896

6997
let mut contains_valid_escape_sequence = false;
70-
let mut invalid_escape_sequence = Vec::new();
98+
let mut invalid_escape_chars = Vec::new();
7199

72100
let mut prev = None;
73101
let bytes = token_source_code.as_bytes();
@@ -154,16 +182,28 @@ pub(crate) fn invalid_escape_sequence(
154182

155183
let location = token_range.start() + TextSize::try_from(i).unwrap();
156184
let range = TextRange::at(location, next_char.text_len() + TextSize::from(1));
157-
invalid_escape_sequence.push(Diagnostic::new(InvalidEscapeSequence(next_char), range));
185+
invalid_escape_chars.push(InvalidEscapeChar {
186+
ch: next_char,
187+
range,
188+
});
158189
}
159190

191+
let mut invalid_escape_sequence = Vec::new();
160192
if contains_valid_escape_sequence {
161193
// Escape with backslash.
162-
for diagnostic in &mut invalid_escape_sequence {
163-
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
194+
for invalid_escape_char in &invalid_escape_chars {
195+
let diagnostic = Diagnostic::new(
196+
InvalidEscapeSequence {
197+
ch: invalid_escape_char.ch,
198+
fix_title: FixTitle::AddBackslash,
199+
},
200+
invalid_escape_char.range,
201+
)
202+
.with_fix(Fix::safe_edit(Edit::insertion(
164203
r"\".to_string(),
165-
diagnostic.start() + TextSize::from(1),
204+
invalid_escape_char.range.start() + TextSize::from(1),
166205
)));
206+
invalid_escape_sequence.push(diagnostic);
167207
}
168208
} else {
169209
let tok_start = if token.is_f_string_middle() {
@@ -178,14 +218,24 @@ pub(crate) fn invalid_escape_sequence(
178218
token_range.start()
179219
};
180220
// Turn into raw string.
181-
for diagnostic in &mut invalid_escape_sequence {
182-
// If necessary, add a space between any leading keyword (`return`, `yield`,
183-
// `assert`, etc.) and the string. For example, `return"foo"` is valid, but
184-
// `returnr"foo"` is not.
185-
diagnostic.set_fix(Fix::safe_edit(Edit::insertion(
186-
pad_start("r".to_string(), tok_start, locator),
187-
tok_start,
188-
)));
221+
for invalid_escape_char in &invalid_escape_chars {
222+
let diagnostic = Diagnostic::new(
223+
InvalidEscapeSequence {
224+
ch: invalid_escape_char.ch,
225+
fix_title: FixTitle::UseRawStringLiteral,
226+
},
227+
invalid_escape_char.range,
228+
)
229+
.with_fix(
230+
// If necessary, add a space between any leading keyword (`return`, `yield`,
231+
// `assert`, etc.) and the string. For example, `return"foo"` is valid, but
232+
// `returnr"foo"` is not.
233+
Fix::safe_edit(Edit::insertion(
234+
pad_start("r".to_string(), tok_start, locator),
235+
tok_start,
236+
)),
237+
);
238+
invalid_escape_sequence.push(diagnostic);
189239
}
190240
}
191241

crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__W605_W605_0.py.snap

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ W605_0.py:2:10: W605 [*] Invalid escape sequence: `\.`
99
3 |
1010
4 | #: W605:2:1
1111
|
12-
= help: Add backslash to escape sequence
12+
= help: Use a raw string literal
1313

1414
Fix
1515
1 1 | #: W605:1:10
@@ -27,7 +27,7 @@ W605_0.py:6:1: W605 [*] Invalid escape sequence: `\.`
2727
| ^^ W605
2828
7 | '''
2929
|
30-
= help: Add backslash to escape sequence
30+
= help: Use a raw string literal
3131

3232
Fix
3333
2 2 | regex = '\.png$'
@@ -47,7 +47,7 @@ W605_0.py:11:6: W605 [*] Invalid escape sequence: `\_`
4747
| ^^ W605
4848
12 | )
4949
|
50-
= help: Add backslash to escape sequence
50+
= help: Use a raw string literal
5151

5252
Fix
5353
8 8 |
@@ -68,7 +68,7 @@ W605_0.py:18:6: W605 [*] Invalid escape sequence: `\_`
6868
19 | in the middle
6969
20 | """
7070
|
71-
= help: Add backslash to escape sequence
71+
= help: Use a raw string literal
7272

7373
Fix
7474
12 12 | )
@@ -107,7 +107,7 @@ W605_0.py:28:12: W605 [*] Invalid escape sequence: `\.`
107107
29 |
108108
30 | #: Okay
109109
|
110-
= help: Add backslash to escape sequence
110+
= help: Use a raw string literal
111111

112112
Fix
113113
25 25 |

crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__W605_W605_1.py.snap

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ W605_1.py:2:10: W605 [*] Invalid escape sequence: `\.`
99
3 |
1010
4 | #: W605:2:1
1111
|
12-
= help: Add backslash to escape sequence
12+
= help: Use a raw string literal
1313

1414
Fix
1515
1 1 | #: W605:1:10
@@ -27,7 +27,7 @@ W605_1.py:6:1: W605 [*] Invalid escape sequence: `\.`
2727
| ^^ W605
2828
7 | '''
2929
|
30-
= help: Add backslash to escape sequence
30+
= help: Use a raw string literal
3131

3232
Fix
3333
2 2 | regex = '\.png$'
@@ -47,7 +47,7 @@ W605_1.py:11:6: W605 [*] Invalid escape sequence: `\_`
4747
| ^^ W605
4848
12 | )
4949
|
50-
= help: Add backslash to escape sequence
50+
= help: Use a raw string literal
5151

5252
Fix
5353
8 8 |
@@ -68,7 +68,7 @@ W605_1.py:18:6: W605 [*] Invalid escape sequence: `\_`
6868
19 | in the middle
6969
20 | """
7070
|
71-
= help: Add backslash to escape sequence
71+
= help: Use a raw string literal
7272

7373
Fix
7474
12 12 | )
@@ -89,7 +89,7 @@ W605_1.py:25:12: W605 [*] Invalid escape sequence: `\.`
8989
26 |
9090
27 | #: Okay
9191
|
92-
= help: Add backslash to escape sequence
92+
= help: Use a raw string literal
9393

9494
Fix
9595
22 22 |

crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__W605_W605_2.py.snap

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
source: crates/ruff/src/rules/pycodestyle/mod.rs
2+
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
33
---
44
W605_2.py:4:11: W605 [*] Invalid escape sequence: `\.`
55
|
@@ -9,7 +9,7 @@ W605_2.py:4:11: W605 [*] Invalid escape sequence: `\.`
99
5 |
1010
6 | #: W605:2:1
1111
|
12-
= help: Add backslash to escape sequence
12+
= help: Use a raw string literal
1313

1414
Fix
1515
1 1 | # Same as `W605_0.py` but using f-strings instead.
@@ -29,7 +29,7 @@ W605_2.py:8:1: W605 [*] Invalid escape sequence: `\.`
2929
| ^^ W605
3030
9 | '''
3131
|
32-
= help: Add backslash to escape sequence
32+
= help: Use a raw string literal
3333

3434
Fix
3535
4 4 | regex = f'\.png$'
@@ -49,7 +49,7 @@ W605_2.py:13:7: W605 [*] Invalid escape sequence: `\_`
4949
| ^^ W605
5050
14 | )
5151
|
52-
= help: Add backslash to escape sequence
52+
= help: Use a raw string literal
5353

5454
Fix
5555
10 10 |
@@ -70,7 +70,7 @@ W605_2.py:20:6: W605 [*] Invalid escape sequence: `\_`
7070
21 | in the middle
7171
22 | """
7272
|
73-
= help: Add backslash to escape sequence
73+
= help: Use a raw string literal
7474

7575
Fix
7676
14 14 | )
@@ -129,7 +129,7 @@ W605_2.py:44:11: W605 [*] Invalid escape sequence: `\{`
129129
45 | value = f'\{1}'
130130
46 | value = f'{1:\}'
131131
|
132-
= help: Add backslash to escape sequence
132+
= help: Use a raw string literal
133133

134134
Fix
135135
41 41 | ''' # noqa
@@ -150,7 +150,7 @@ W605_2.py:45:11: W605 [*] Invalid escape sequence: `\{`
150150
46 | value = f'{1:\}'
151151
47 | value = f"{f"\{1}"}"
152152
|
153-
= help: Add backslash to escape sequence
153+
= help: Use a raw string literal
154154

155155
Fix
156156
42 42 |
@@ -171,7 +171,7 @@ W605_2.py:46:14: W605 [*] Invalid escape sequence: `\}`
171171
47 | value = f"{f"\{1}"}"
172172
48 | value = rf"{f"\{1}"}"
173173
|
174-
= help: Add backslash to escape sequence
174+
= help: Use a raw string literal
175175

176176
Fix
177177
43 43 | regex = f'\\\_'
@@ -191,7 +191,7 @@ W605_2.py:47:14: W605 [*] Invalid escape sequence: `\{`
191191
| ^^ W605
192192
48 | value = rf"{f"\{1}"}"
193193
|
194-
= help: Add backslash to escape sequence
194+
= help: Use a raw string literal
195195

196196
Fix
197197
44 44 | value = f'\{{1}}'
@@ -212,7 +212,7 @@ W605_2.py:48:15: W605 [*] Invalid escape sequence: `\{`
212212
49 |
213213
50 | # Okay
214214
|
215-
= help: Add backslash to escape sequence
215+
= help: Use a raw string literal
216216

217217
Fix
218218
45 45 | value = f'\{1}'

0 commit comments

Comments
 (0)