diff --git a/crates/ruff/src/checkers/logical_lines.rs b/crates/ruff/src/checkers/logical_lines.rs index 2a5b0c09f3..cbf9263c70 100644 --- a/crates/ruff/src/checkers/logical_lines.rs +++ b/crates/ruff/src/checkers/logical_lines.rs @@ -39,9 +39,15 @@ pub(crate) fn check_logical_lines( let mut context = LogicalLinesContext::new(settings); let should_fix_missing_whitespace = settings.rules.should_fix(Rule::MissingWhitespace); - let should_fix_whitespace_before_parameters = settings.rules.should_fix(Rule::WhitespaceBeforeParameters); + let should_fix_whitespace_after_open_bracket = + settings.rules.should_fix(Rule::WhitespaceAfterOpenBracket); + let should_fix_whitespace_before_close_bracket = settings + .rules + .should_fix(Rule::WhitespaceBeforeCloseBracket); + let should_fix_whitespace_before_punctuation = + settings.rules.should_fix(Rule::WhitespaceBeforePunctuation); let mut prev_line = None; let mut prev_indent_level = None; @@ -59,7 +65,13 @@ pub(crate) fn check_logical_lines( .flags() .intersects(TokenFlags::OPERATOR | TokenFlags::BRACKET | TokenFlags::PUNCTUATION) { - extraneous_whitespace(&line, &mut context); + extraneous_whitespace( + &line, + &mut context, + should_fix_whitespace_after_open_bracket, + should_fix_whitespace_before_close_bracket, + should_fix_whitespace_before_punctuation, + ); } if line.flags().contains(TokenFlags::KEYWORD) { diff --git a/crates/ruff/src/rules/pycodestyle/rules/logical_lines/extraneous_whitespace.rs b/crates/ruff/src/rules/pycodestyle/rules/logical_lines/extraneous_whitespace.rs index cf8240ded7..d8a3512cf7 100644 --- a/crates/ruff/src/rules/pycodestyle/rules/logical_lines/extraneous_whitespace.rs +++ b/crates/ruff/src/rules/pycodestyle/rules/logical_lines/extraneous_whitespace.rs @@ -1,9 +1,10 @@ -use ruff_text_size::TextRange; - -use ruff_diagnostics::DiagnosticKind; -use ruff_diagnostics::Violation; +use ruff_diagnostics::AlwaysAutofixableViolation; +use ruff_diagnostics::Diagnostic; +use ruff_diagnostics::Edit; +use ruff_diagnostics::Fix; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::token_kind::TokenKind; +use ruff_text_size::TextRange; use crate::checkers::logical_lines::LogicalLinesContext; @@ -36,12 +37,17 @@ pub struct WhitespaceAfterOpenBracket { symbol: char, } -impl Violation for WhitespaceAfterOpenBracket { +impl AlwaysAutofixableViolation for WhitespaceAfterOpenBracket { #[derive_message_formats] fn message(&self) -> String { let WhitespaceAfterOpenBracket { symbol } = self; format!("Whitespace after '{symbol}'") } + + fn autofix_title(&self) -> String { + let WhitespaceAfterOpenBracket { symbol } = self; + format!("Remove whitespace before '{symbol}'") + } } /// ## What it does @@ -71,12 +77,17 @@ pub struct WhitespaceBeforeCloseBracket { symbol: char, } -impl Violation for WhitespaceBeforeCloseBracket { +impl AlwaysAutofixableViolation for WhitespaceBeforeCloseBracket { #[derive_message_formats] fn message(&self) -> String { let WhitespaceBeforeCloseBracket { symbol } = self; format!("Whitespace before '{symbol}'") } + + fn autofix_title(&self) -> String { + let WhitespaceBeforeCloseBracket { symbol } = self; + format!("Remove whitespace before '{symbol}'") + } } /// ## What it does @@ -104,16 +115,27 @@ pub struct WhitespaceBeforePunctuation { symbol: char, } -impl Violation for WhitespaceBeforePunctuation { +impl AlwaysAutofixableViolation for WhitespaceBeforePunctuation { #[derive_message_formats] fn message(&self) -> String { let WhitespaceBeforePunctuation { symbol } = self; format!("Whitespace before '{symbol}'") } + + fn autofix_title(&self) -> String { + let WhitespaceBeforePunctuation { symbol } = self; + format!("Remove whitespace before '{symbol}'") + } } /// E201, E202, E203 -pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &mut LogicalLinesContext) { +pub(crate) fn extraneous_whitespace( + line: &LogicalLine, + context: &mut LogicalLinesContext, + autofix_after_open_bracket: bool, + autofix_before_close_bracket: bool, + autofix_before_punctuation: bool, +) { let mut prev_token = None; for token in line.tokens() { @@ -123,10 +145,15 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &mut LogicalLin BracketOrPunctuation::OpenBracket(symbol) => { let (trailing, trailing_len) = line.trailing_whitespace(token); if !matches!(trailing, Whitespace::None) { - context.push( + let mut diagnostic = Diagnostic::new( WhitespaceAfterOpenBracket { symbol }, TextRange::at(token.end(), trailing_len), ); + if autofix_after_open_bracket { + diagnostic + .set_fix(Fix::automatic(Edit::range_deletion(diagnostic.range()))); + } + context.push_diagnostic(diagnostic); } } BracketOrPunctuation::CloseBracket(symbol) => { @@ -134,10 +161,16 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &mut LogicalLin if let (Whitespace::Single | Whitespace::Many | Whitespace::Tab, offset) = line.leading_whitespace(token) { - context.push( - DiagnosticKind::from(WhitespaceBeforeCloseBracket { symbol }), + let mut diagnostic = Diagnostic::new( + WhitespaceBeforeCloseBracket { symbol }, TextRange::at(token.start() - offset, offset), ); + if autofix_before_close_bracket { + diagnostic.set_fix(Fix::automatic(Edit::range_deletion( + diagnostic.range(), + ))); + } + context.push_diagnostic(diagnostic); } } } @@ -146,10 +179,16 @@ pub(crate) fn extraneous_whitespace(line: &LogicalLine, context: &mut LogicalLin if let (Whitespace::Single | Whitespace::Many | Whitespace::Tab, offset) = line.leading_whitespace(token) { - context.push( - DiagnosticKind::from(WhitespaceBeforePunctuation { symbol }), + let mut diagnostic = Diagnostic::new( + WhitespaceBeforePunctuation { symbol }, TextRange::at(token.start() - offset, offset), ); + if autofix_before_punctuation { + diagnostic.set_fix(Fix::automatic(Edit::range_deletion( + diagnostic.range(), + ))); + } + context.push_diagnostic(diagnostic); } } } diff --git a/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E201_E20.py.snap b/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E201_E20.py.snap index ae2f33c06d..ae56ec6e68 100644 --- a/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E201_E20.py.snap +++ b/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E201_E20.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff/src/rules/pycodestyle/mod.rs --- -E20.py:2:6: E201 Whitespace after '(' +E20.py:2:6: E201 [*] Whitespace after '(' | 2 | #: E201:1:6 3 | spam( ham[1], {eggs: 2}) @@ -9,8 +9,17 @@ E20.py:2:6: E201 Whitespace after '(' 4 | #: E201:1:10 5 | spam(ham[ 1], {eggs: 2}) | + = help: Remove whitespace before '(' -E20.py:4:10: E201 Whitespace after '[' +ℹ Fix +1 1 | #: E201:1:6 +2 |-spam( ham[1], {eggs: 2}) + 2 |+spam(ham[1], {eggs: 2}) +3 3 | #: E201:1:10 +4 4 | spam(ham[ 1], {eggs: 2}) +5 5 | #: E201:1:15 + +E20.py:4:10: E201 [*] Whitespace after '[' | 4 | spam( ham[1], {eggs: 2}) 5 | #: E201:1:10 @@ -19,8 +28,19 @@ E20.py:4:10: E201 Whitespace after '[' 7 | #: E201:1:15 8 | spam(ham[1], { eggs: 2}) | + = help: Remove whitespace before '[' -E20.py:6:15: E201 Whitespace after '{' +ℹ Fix +1 1 | #: E201:1:6 +2 2 | spam( ham[1], {eggs: 2}) +3 3 | #: E201:1:10 +4 |-spam(ham[ 1], {eggs: 2}) + 4 |+spam(ham[1], {eggs: 2}) +5 5 | #: E201:1:15 +6 6 | spam(ham[1], { eggs: 2}) +7 7 | #: E201:1:6 + +E20.py:6:15: E201 [*] Whitespace after '{' | 6 | spam(ham[ 1], {eggs: 2}) 7 | #: E201:1:15 @@ -29,8 +49,19 @@ E20.py:6:15: E201 Whitespace after '{' 9 | #: E201:1:6 10 | spam( ham[1], {eggs: 2}) | + = help: Remove whitespace before '{' -E20.py:8:6: E201 Whitespace after '(' +ℹ Fix +3 3 | #: E201:1:10 +4 4 | spam(ham[ 1], {eggs: 2}) +5 5 | #: E201:1:15 +6 |-spam(ham[1], { eggs: 2}) + 6 |+spam(ham[1], {eggs: 2}) +7 7 | #: E201:1:6 +8 8 | spam( ham[1], {eggs: 2}) +9 9 | #: E201:1:10 + +E20.py:8:6: E201 [*] Whitespace after '(' | 8 | spam(ham[1], { eggs: 2}) 9 | #: E201:1:6 @@ -39,8 +70,19 @@ E20.py:8:6: E201 Whitespace after '(' 11 | #: E201:1:10 12 | spam(ham[ 1], {eggs: 2}) | + = help: Remove whitespace before '(' -E20.py:10:10: E201 Whitespace after '[' +ℹ Fix +5 5 | #: E201:1:15 +6 6 | spam(ham[1], { eggs: 2}) +7 7 | #: E201:1:6 +8 |-spam( ham[1], {eggs: 2}) + 8 |+spam(ham[1], {eggs: 2}) +9 9 | #: E201:1:10 +10 10 | spam(ham[ 1], {eggs: 2}) +11 11 | #: E201:1:15 + +E20.py:10:10: E201 [*] Whitespace after '[' | 10 | spam( ham[1], {eggs: 2}) 11 | #: E201:1:10 @@ -49,8 +91,19 @@ E20.py:10:10: E201 Whitespace after '[' 13 | #: E201:1:15 14 | spam(ham[1], { eggs: 2}) | + = help: Remove whitespace before '[' -E20.py:12:15: E201 Whitespace after '{' +ℹ Fix +7 7 | #: E201:1:6 +8 8 | spam( ham[1], {eggs: 2}) +9 9 | #: E201:1:10 +10 |-spam(ham[ 1], {eggs: 2}) + 10 |+spam(ham[1], {eggs: 2}) +11 11 | #: E201:1:15 +12 12 | spam(ham[1], { eggs: 2}) +13 13 | #: Okay + +E20.py:12:15: E201 [*] Whitespace after '{' | 12 | spam(ham[ 1], {eggs: 2}) 13 | #: E201:1:15 @@ -59,8 +112,19 @@ E20.py:12:15: E201 Whitespace after '{' 15 | #: Okay 16 | spam(ham[1], {eggs: 2}) | + = help: Remove whitespace before '{' -E20.py:81:6: E201 Whitespace after '[' +ℹ Fix +9 9 | #: E201:1:10 +10 10 | spam(ham[ 1], {eggs: 2}) +11 11 | #: E201:1:15 +12 |-spam(ham[1], { eggs: 2}) + 12 |+spam(ham[1], {eggs: 2}) +13 13 | #: Okay +14 14 | spam(ham[1], {eggs: 2}) +15 15 | #: + +E20.py:81:6: E201 [*] Whitespace after '[' | 81 | #: E201:1:6 82 | spam[ ~ham] @@ -68,5 +132,16 @@ E20.py:81:6: E201 Whitespace after '[' 83 | 84 | #: Okay | + = help: Remove whitespace before '[' + +ℹ Fix +78 78 | #: +79 79 | +80 80 | #: E201:1:6 +81 |-spam[ ~ham] + 81 |+spam[~ham] +82 82 | +83 83 | #: Okay +84 84 | x = [ # diff --git a/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E202_E20.py.snap b/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E202_E20.py.snap index 72cd783c5b..5218395566 100644 --- a/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E202_E20.py.snap +++ b/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E202_E20.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff/src/rules/pycodestyle/mod.rs --- -E20.py:19:23: E202 Whitespace before ')' +E20.py:19:23: E202 [*] Whitespace before ')' | 19 | #: E202:1:23 20 | spam(ham[1], {eggs: 2} ) @@ -9,8 +9,19 @@ E20.py:19:23: E202 Whitespace before ')' 21 | #: E202:1:22 22 | spam(ham[1], {eggs: 2 }) | + = help: Remove whitespace before ')' -E20.py:21:22: E202 Whitespace before '}' +ℹ Fix +16 16 | +17 17 | +18 18 | #: E202:1:23 +19 |-spam(ham[1], {eggs: 2} ) + 19 |+spam(ham[1], {eggs: 2}) +20 20 | #: E202:1:22 +21 21 | spam(ham[1], {eggs: 2 }) +22 22 | #: E202:1:11 + +E20.py:21:22: E202 [*] Whitespace before '}' | 21 | spam(ham[1], {eggs: 2} ) 22 | #: E202:1:22 @@ -19,8 +30,19 @@ E20.py:21:22: E202 Whitespace before '}' 24 | #: E202:1:11 25 | spam(ham[1 ], {eggs: 2}) | + = help: Remove whitespace before '}' -E20.py:23:11: E202 Whitespace before ']' +ℹ Fix +18 18 | #: E202:1:23 +19 19 | spam(ham[1], {eggs: 2} ) +20 20 | #: E202:1:22 +21 |-spam(ham[1], {eggs: 2 }) + 21 |+spam(ham[1], {eggs: 2}) +22 22 | #: E202:1:11 +23 23 | spam(ham[1 ], {eggs: 2}) +24 24 | #: E202:1:23 + +E20.py:23:11: E202 [*] Whitespace before ']' | 23 | spam(ham[1], {eggs: 2 }) 24 | #: E202:1:11 @@ -29,8 +51,19 @@ E20.py:23:11: E202 Whitespace before ']' 26 | #: E202:1:23 27 | spam(ham[1], {eggs: 2} ) | + = help: Remove whitespace before ']' -E20.py:25:23: E202 Whitespace before ')' +ℹ Fix +20 20 | #: E202:1:22 +21 21 | spam(ham[1], {eggs: 2 }) +22 22 | #: E202:1:11 +23 |-spam(ham[1 ], {eggs: 2}) + 23 |+spam(ham[1], {eggs: 2}) +24 24 | #: E202:1:23 +25 25 | spam(ham[1], {eggs: 2} ) +26 26 | #: E202:1:22 + +E20.py:25:23: E202 [*] Whitespace before ')' | 25 | spam(ham[1 ], {eggs: 2}) 26 | #: E202:1:23 @@ -39,8 +72,19 @@ E20.py:25:23: E202 Whitespace before ')' 28 | #: E202:1:22 29 | spam(ham[1], {eggs: 2 }) | + = help: Remove whitespace before ')' -E20.py:27:22: E202 Whitespace before '}' +ℹ Fix +22 22 | #: E202:1:11 +23 23 | spam(ham[1 ], {eggs: 2}) +24 24 | #: E202:1:23 +25 |-spam(ham[1], {eggs: 2} ) + 25 |+spam(ham[1], {eggs: 2}) +26 26 | #: E202:1:22 +27 27 | spam(ham[1], {eggs: 2 }) +28 28 | #: E202:1:11 + +E20.py:27:22: E202 [*] Whitespace before '}' | 27 | spam(ham[1], {eggs: 2} ) 28 | #: E202:1:22 @@ -49,8 +93,19 @@ E20.py:27:22: E202 Whitespace before '}' 30 | #: E202:1:11 31 | spam(ham[1 ], {eggs: 2}) | + = help: Remove whitespace before '}' -E20.py:29:11: E202 Whitespace before ']' +ℹ Fix +24 24 | #: E202:1:23 +25 25 | spam(ham[1], {eggs: 2} ) +26 26 | #: E202:1:22 +27 |-spam(ham[1], {eggs: 2 }) + 27 |+spam(ham[1], {eggs: 2}) +28 28 | #: E202:1:11 +29 29 | spam(ham[1 ], {eggs: 2}) +30 30 | #: Okay + +E20.py:29:11: E202 [*] Whitespace before ']' | 29 | spam(ham[1], {eggs: 2 }) 30 | #: E202:1:11 @@ -59,5 +114,16 @@ E20.py:29:11: E202 Whitespace before ']' 32 | #: Okay 33 | spam(ham[1], {eggs: 2}) | + = help: Remove whitespace before ']' + +ℹ Fix +26 26 | #: E202:1:22 +27 27 | spam(ham[1], {eggs: 2 }) +28 28 | #: E202:1:11 +29 |-spam(ham[1 ], {eggs: 2}) + 29 |+spam(ham[1], {eggs: 2}) +30 30 | #: Okay +31 31 | spam(ham[1], {eggs: 2}) +32 32 | diff --git a/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E203_E20.py.snap b/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E203_E20.py.snap index e5715034c8..8a1524c2e7 100644 --- a/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E203_E20.py.snap +++ b/crates/ruff/src/rules/pycodestyle/snapshots/ruff__rules__pycodestyle__tests__E203_E20.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff/src/rules/pycodestyle/mod.rs --- -E20.py:51:10: E203 Whitespace before ':' +E20.py:51:10: E203 [*] Whitespace before ':' | 51 | #: E203:1:10 52 | if x == 4 : @@ -9,8 +9,19 @@ E20.py:51:10: E203 Whitespace before ':' 53 | print x, y 54 | x, y = y, x | + = help: Remove whitespace before ':' -E20.py:55:10: E203 Whitespace before ':' +ℹ Fix +48 48 | +49 49 | +50 50 | #: E203:1:10 +51 |-if x == 4 : + 51 |+if x == 4: +52 52 | print x, y +53 53 | x, y = y, x +54 54 | #: E203:1:10 + +E20.py:55:10: E203 [*] Whitespace before ':' | 55 | x, y = y, x 56 | #: E203:1:10 @@ -19,8 +30,19 @@ E20.py:55:10: E203 Whitespace before ':' 58 | print x, y 59 | x, y = y, x | + = help: Remove whitespace before ':' -E20.py:60:15: E203 Whitespace before ';' +ℹ Fix +52 52 | print x, y +53 53 | x, y = y, x +54 54 | #: E203:1:10 +55 |-if x == 4 : + 55 |+if x == 4: +56 56 | print x, y +57 57 | x, y = y, x +58 58 | #: E203:2:15 E702:2:16 + +E20.py:60:15: E203 [*] Whitespace before ';' | 60 | #: E203:2:15 E702:2:16 61 | if x == 4: @@ -29,8 +51,19 @@ E20.py:60:15: E203 Whitespace before ';' 63 | #: E203:2:15 E702:2:16 64 | if x == 4: | + = help: Remove whitespace before ';' -E20.py:63:15: E203 Whitespace before ';' +ℹ Fix +57 57 | x, y = y, x +58 58 | #: E203:2:15 E702:2:16 +59 59 | if x == 4: +60 |- print x, y ; x, y = y, x + 60 |+ print x, y; x, y = y, x +61 61 | #: E203:2:15 E702:2:16 +62 62 | if x == 4: +63 63 | print x, y ; x, y = y, x + +E20.py:63:15: E203 [*] Whitespace before ';' | 63 | #: E203:2:15 E702:2:16 64 | if x == 4: @@ -39,8 +72,19 @@ E20.py:63:15: E203 Whitespace before ';' 66 | #: E203:3:13 67 | if x == 4: | + = help: Remove whitespace before ';' -E20.py:67:13: E203 Whitespace before ',' +ℹ Fix +60 60 | print x, y ; x, y = y, x +61 61 | #: E203:2:15 E702:2:16 +62 62 | if x == 4: +63 |- print x, y ; x, y = y, x + 63 |+ print x, y; x, y = y, x +64 64 | #: E203:3:13 +65 65 | if x == 4: +66 66 | print x, y + +E20.py:67:13: E203 [*] Whitespace before ',' | 67 | if x == 4: 68 | print x, y @@ -49,8 +93,19 @@ E20.py:67:13: E203 Whitespace before ',' 70 | #: E203:3:13 71 | if x == 4: | + = help: Remove whitespace before ',' -E20.py:71:13: E203 Whitespace before ',' +ℹ Fix +64 64 | #: E203:3:13 +65 65 | if x == 4: +66 66 | print x, y +67 |- x, y = y , x + 67 |+ x, y = y, x +68 68 | #: E203:3:13 +69 69 | if x == 4: +70 70 | print x, y + +E20.py:71:13: E203 [*] Whitespace before ',' | 71 | if x == 4: 72 | print x, y @@ -59,5 +114,16 @@ E20.py:71:13: E203 Whitespace before ',' 74 | #: Okay 75 | if x == 4: | + = help: Remove whitespace before ',' + +ℹ Fix +68 68 | #: E203:3:13 +69 69 | if x == 4: +70 70 | print x, y +71 |- x, y = y , x + 71 |+ x, y = y, x +72 72 | #: Okay +73 73 | if x == 4: +74 74 | print x, y