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
This commit is contained in:
Dhruv Manilawala 2023-10-26 21:53:20 +05:30 committed by GitHub
parent a4dd1e5fad
commit 4ffd4ed61f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 38 deletions

View file

@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
use ruff_python_index::Indexer; use ruff_python_index::Indexer;
use ruff_python_parser::Tok; use ruff_python_parser::Tok;
use ruff_source_file::Locator; use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize}; use ruff_text_size::{TextLen, TextRange, TextSize};
use crate::fix::edits::pad_start; use crate::fix::edits::pad_start;
@ -25,22 +25,50 @@ use crate::fix::edits::pad_start;
/// regex = r"\.png$" /// regex = r"\.png$"
/// ``` /// ```
/// ///
/// Or, if the string already contains a valid escape sequence:
/// ```python
/// value = "new line\nand invalid escape \_ here"
/// ```
///
/// Use instead:
/// ```python
/// value = "new line\nand invalid escape \\_ here"
/// ```
///
/// ## References /// ## References
/// - [Python documentation: String and Bytes literals](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals) /// - [Python documentation: String and Bytes literals](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals)
#[violation] #[violation]
pub struct InvalidEscapeSequence(char); pub struct InvalidEscapeSequence {
ch: char,
fix_title: FixTitle,
}
impl AlwaysFixableViolation for InvalidEscapeSequence { impl AlwaysFixableViolation for InvalidEscapeSequence {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let InvalidEscapeSequence(char) = self; let InvalidEscapeSequence { ch, .. } = self;
format!("Invalid escape sequence: `\\{char}`") format!("Invalid escape sequence: `\\{ch}`")
} }
fn fix_title(&self) -> String { fn fix_title(&self) -> String {
"Add backslash to escape sequence".to_string() match self.fix_title {
FixTitle::AddBackslash => format!("Add backslash to escape sequence"),
FixTitle::UseRawStringLiteral => format!("Use a raw string literal"),
} }
} }
}
#[derive(Debug, PartialEq, Eq)]
enum FixTitle {
AddBackslash,
UseRawStringLiteral,
}
#[derive(Debug)]
struct InvalidEscapeChar {
ch: char,
range: TextRange,
}
/// W605 /// W605
pub(crate) fn invalid_escape_sequence( pub(crate) fn invalid_escape_sequence(
@ -67,7 +95,7 @@ pub(crate) fn invalid_escape_sequence(
}; };
let mut contains_valid_escape_sequence = false; let mut contains_valid_escape_sequence = false;
let mut invalid_escape_sequence = Vec::new(); let mut invalid_escape_chars = Vec::new();
let mut prev = None; let mut prev = None;
let bytes = token_source_code.as_bytes(); let bytes = token_source_code.as_bytes();
@ -154,16 +182,28 @@ pub(crate) fn invalid_escape_sequence(
let location = token_range.start() + TextSize::try_from(i).unwrap(); let location = token_range.start() + TextSize::try_from(i).unwrap();
let range = TextRange::at(location, next_char.text_len() + TextSize::from(1)); let range = TextRange::at(location, next_char.text_len() + TextSize::from(1));
invalid_escape_sequence.push(Diagnostic::new(InvalidEscapeSequence(next_char), range)); invalid_escape_chars.push(InvalidEscapeChar {
ch: next_char,
range,
});
} }
let mut invalid_escape_sequence = Vec::new();
if contains_valid_escape_sequence { if contains_valid_escape_sequence {
// Escape with backslash. // Escape with backslash.
for diagnostic in &mut invalid_escape_sequence { for invalid_escape_char in &invalid_escape_chars {
diagnostic.set_fix(Fix::safe_edit(Edit::insertion( let diagnostic = Diagnostic::new(
InvalidEscapeSequence {
ch: invalid_escape_char.ch,
fix_title: FixTitle::AddBackslash,
},
invalid_escape_char.range,
)
.with_fix(Fix::safe_edit(Edit::insertion(
r"\".to_string(), r"\".to_string(),
diagnostic.start() + TextSize::from(1), invalid_escape_char.range.start() + TextSize::from(1),
))); )));
invalid_escape_sequence.push(diagnostic);
} }
} else { } else {
let tok_start = if token.is_f_string_middle() { let tok_start = if token.is_f_string_middle() {
@ -178,14 +218,24 @@ pub(crate) fn invalid_escape_sequence(
token_range.start() token_range.start()
}; };
// Turn into raw string. // Turn into raw string.
for diagnostic in &mut invalid_escape_sequence { for invalid_escape_char in &invalid_escape_chars {
let diagnostic = Diagnostic::new(
InvalidEscapeSequence {
ch: invalid_escape_char.ch,
fix_title: FixTitle::UseRawStringLiteral,
},
invalid_escape_char.range,
)
.with_fix(
// If necessary, add a space between any leading keyword (`return`, `yield`, // If necessary, add a space between any leading keyword (`return`, `yield`,
// `assert`, etc.) and the string. For example, `return"foo"` is valid, but // `assert`, etc.) and the string. For example, `return"foo"` is valid, but
// `returnr"foo"` is not. // `returnr"foo"` is not.
diagnostic.set_fix(Fix::safe_edit(Edit::insertion( Fix::safe_edit(Edit::insertion(
pad_start("r".to_string(), tok_start, locator), pad_start("r".to_string(), tok_start, locator),
tok_start, tok_start,
))); )),
);
invalid_escape_sequence.push(diagnostic);
} }
} }

View file

@ -9,7 +9,7 @@ W605_0.py:2:10: W605 [*] Invalid escape sequence: `\.`
3 | 3 |
4 | #: W605:2:1 4 | #: W605:2:1
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
1 1 | #: W605:1:10 1 1 | #: W605:1:10
@ -27,7 +27,7 @@ W605_0.py:6:1: W605 [*] Invalid escape sequence: `\.`
| ^^ W605 | ^^ W605
7 | ''' 7 | '''
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
2 2 | regex = '\.png$' 2 2 | regex = '\.png$'
@ -47,7 +47,7 @@ W605_0.py:11:6: W605 [*] Invalid escape sequence: `\_`
| ^^ W605 | ^^ W605
12 | ) 12 | )
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
8 8 | 8 8 |
@ -68,7 +68,7 @@ W605_0.py:18:6: W605 [*] Invalid escape sequence: `\_`
19 | in the middle 19 | in the middle
20 | """ 20 | """
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
12 12 | ) 12 12 | )
@ -107,7 +107,7 @@ W605_0.py:28:12: W605 [*] Invalid escape sequence: `\.`
29 | 29 |
30 | #: Okay 30 | #: Okay
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
25 25 | 25 25 |

View file

@ -9,7 +9,7 @@ W605_1.py:2:10: W605 [*] Invalid escape sequence: `\.`
3 | 3 |
4 | #: W605:2:1 4 | #: W605:2:1
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
1 1 | #: W605:1:10 1 1 | #: W605:1:10
@ -27,7 +27,7 @@ W605_1.py:6:1: W605 [*] Invalid escape sequence: `\.`
| ^^ W605 | ^^ W605
7 | ''' 7 | '''
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
2 2 | regex = '\.png$' 2 2 | regex = '\.png$'
@ -47,7 +47,7 @@ W605_1.py:11:6: W605 [*] Invalid escape sequence: `\_`
| ^^ W605 | ^^ W605
12 | ) 12 | )
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
8 8 | 8 8 |
@ -68,7 +68,7 @@ W605_1.py:18:6: W605 [*] Invalid escape sequence: `\_`
19 | in the middle 19 | in the middle
20 | """ 20 | """
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
12 12 | ) 12 12 | )
@ -89,7 +89,7 @@ W605_1.py:25:12: W605 [*] Invalid escape sequence: `\.`
26 | 26 |
27 | #: Okay 27 | #: Okay
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
22 22 | 22 22 |

View file

@ -1,5 +1,5 @@
--- ---
source: crates/ruff/src/rules/pycodestyle/mod.rs source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
--- ---
W605_2.py:4:11: W605 [*] Invalid escape sequence: `\.` W605_2.py:4:11: W605 [*] Invalid escape sequence: `\.`
| |
@ -9,7 +9,7 @@ W605_2.py:4:11: W605 [*] Invalid escape sequence: `\.`
5 | 5 |
6 | #: W605:2:1 6 | #: W605:2:1
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
1 1 | # Same as `W605_0.py` but using f-strings instead. 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: `\.`
| ^^ W605 | ^^ W605
9 | ''' 9 | '''
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
4 4 | regex = f'\.png$' 4 4 | regex = f'\.png$'
@ -49,7 +49,7 @@ W605_2.py:13:7: W605 [*] Invalid escape sequence: `\_`
| ^^ W605 | ^^ W605
14 | ) 14 | )
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
10 10 | 10 10 |
@ -70,7 +70,7 @@ W605_2.py:20:6: W605 [*] Invalid escape sequence: `\_`
21 | in the middle 21 | in the middle
22 | """ 22 | """
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
14 14 | ) 14 14 | )
@ -129,7 +129,7 @@ W605_2.py:44:11: W605 [*] Invalid escape sequence: `\{`
45 | value = f'\{1}' 45 | value = f'\{1}'
46 | value = f'{1:\}' 46 | value = f'{1:\}'
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
41 41 | ''' # noqa 41 41 | ''' # noqa
@ -150,7 +150,7 @@ W605_2.py:45:11: W605 [*] Invalid escape sequence: `\{`
46 | value = f'{1:\}' 46 | value = f'{1:\}'
47 | value = f"{f"\{1}"}" 47 | value = f"{f"\{1}"}"
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
42 42 | 42 42 |
@ -171,7 +171,7 @@ W605_2.py:46:14: W605 [*] Invalid escape sequence: `\}`
47 | value = f"{f"\{1}"}" 47 | value = f"{f"\{1}"}"
48 | value = rf"{f"\{1}"}" 48 | value = rf"{f"\{1}"}"
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
43 43 | regex = f'\\\_' 43 43 | regex = f'\\\_'
@ -191,7 +191,7 @@ W605_2.py:47:14: W605 [*] Invalid escape sequence: `\{`
| ^^ W605 | ^^ W605
48 | value = rf"{f"\{1}"}" 48 | value = rf"{f"\{1}"}"
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
44 44 | value = f'\{{1}}' 44 44 | value = f'\{{1}}'
@ -212,7 +212,7 @@ W605_2.py:48:15: W605 [*] Invalid escape sequence: `\{`
49 | 49 |
50 | # Okay 50 | # Okay
| |
= help: Add backslash to escape sequence = help: Use a raw string literal
Fix Fix
45 45 | value = f'\{1}' 45 45 | value = f'\{1}'