From e0a0ddcf7d1ee74e95a679ea6e9c842f1dcbfa69 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sat, 11 Nov 2023 15:41:12 -0800 Subject: [PATCH] Implement autofix for `multiple-spaces-after-keyword` and `multiple-spaces-before-keyword` (#8622) Closes https://github.com/astral-sh/ruff/issues/8312. --- .../whitespace_around_keywords.rs | 31 ++++- ...ules__pycodestyle__tests__E271_E27.py.snap | 117 ++++++++++++++++-- ...ules__pycodestyle__tests__E272_E27.py.snap | 39 +++++- 3 files changed, 170 insertions(+), 17 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/whitespace_around_keywords.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/whitespace_around_keywords.rs index cc3e86cf8be6a..365060f41d0e5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/whitespace_around_keywords.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/whitespace_around_keywords.rs @@ -1,4 +1,4 @@ -use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, Violation}; +use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_text_size::{Ranged, TextRange}; @@ -24,11 +24,15 @@ use super::{LogicalLine, Whitespace}; #[violation] pub struct MultipleSpacesAfterKeyword; -impl Violation for MultipleSpacesAfterKeyword { +impl AlwaysFixableViolation for MultipleSpacesAfterKeyword { #[derive_message_formats] fn message(&self) -> String { format!("Multiple spaces after keyword") } + + fn fix_title(&self) -> String { + format!("Replace with single space") + } } /// ## What it does @@ -49,11 +53,15 @@ impl Violation for MultipleSpacesAfterKeyword { #[violation] pub struct MultipleSpacesBeforeKeyword; -impl Violation for MultipleSpacesBeforeKeyword { +impl AlwaysFixableViolation for MultipleSpacesBeforeKeyword { #[derive_message_formats] fn message(&self) -> String { format!("Multiple spaces before keyword") } + + fn fix_title(&self) -> String { + format!("Replace with single space") + } } /// ## What it does @@ -137,10 +145,15 @@ pub(crate) fn whitespace_around_keywords(line: &LogicalLine, context: &mut Logic } (Whitespace::Many, offset) => { let start = token.start(); - context.push( + let mut diagnostic = Diagnostic::new( MultipleSpacesBeforeKeyword, TextRange::at(start - offset, offset), ); + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + " ".to_string(), + TextRange::at(start - offset, offset), + ))); + context.push_diagnostic(diagnostic); } _ => {} } @@ -157,7 +170,15 @@ pub(crate) fn whitespace_around_keywords(line: &LogicalLine, context: &mut Logic context.push_diagnostic(diagnostic); } (Whitespace::Many, len) => { - context.push(MultipleSpacesAfterKeyword, TextRange::at(token.end(), len)); + let mut diagnostic = Diagnostic::new( + MultipleSpacesAfterKeyword, + TextRange::at(token.end(), len), + ); + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + " ".to_string(), + TextRange::at(token.end(), len), + ))); + context.push_diagnostic(diagnostic); } _ => {} } diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E271_E27.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E271_E27.py.snap index 314cdb4a6c83b..769525c450820 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E271_E27.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E271_E27.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E27.py:4:9: E271 Multiple spaces after keyword +E27.py:4:9: E271 [*] Multiple spaces after keyword | 2 | True and False 3 | #: E271 @@ -10,8 +10,19 @@ E27.py:4:9: E271 Multiple spaces after keyword 5 | #: E272 6 | True and False | + = help: Replace with single space -E27.py:6:5: E271 Multiple spaces after keyword +ℹ Safe fix +1 1 | #: Okay +2 2 | True and False +3 3 | #: E271 +4 |-True and False + 4 |+True and False +5 5 | #: E272 +6 6 | True and False +7 7 | #: E271 + +E27.py:6:5: E271 [*] Multiple spaces after keyword | 4 | True and False 5 | #: E272 @@ -20,8 +31,19 @@ E27.py:6:5: E271 Multiple spaces after keyword 7 | #: E271 8 | if 1: | + = help: Replace with single space + +ℹ Safe fix +3 3 | #: E271 +4 4 | True and False +5 5 | #: E272 +6 |-True and False + 6 |+True and False +7 7 | #: E271 +8 8 | if 1: +9 9 | #: E273 -E27.py:8:3: E271 Multiple spaces after keyword +E27.py:8:3: E271 [*] Multiple spaces after keyword | 6 | True and False 7 | #: E271 @@ -30,8 +52,19 @@ E27.py:8:3: E271 Multiple spaces after keyword 9 | #: E273 10 | True and False | + = help: Replace with single space + +ℹ Safe fix +5 5 | #: E272 +6 6 | True and False +7 7 | #: E271 +8 |-if 1: + 8 |+if 1: +9 9 | #: E273 +10 10 | True and False +11 11 | #: E273 E274 -E27.py:14:6: E271 Multiple spaces after keyword +E27.py:14:6: E271 [*] Multiple spaces after keyword | 12 | True and False 13 | #: E271 @@ -40,8 +73,19 @@ E27.py:14:6: E271 Multiple spaces after keyword 15 | #: E271 16 | 1 and b | + = help: Replace with single space -E27.py:16:6: E271 Multiple spaces after keyword +ℹ Safe fix +11 11 | #: E273 E274 +12 12 | True and False +13 13 | #: E271 +14 |-a and b + 14 |+a and b +15 15 | #: E271 +16 16 | 1 and b +17 17 | #: E271 + +E27.py:16:6: E271 [*] Multiple spaces after keyword | 14 | a and b 15 | #: E271 @@ -50,8 +94,19 @@ E27.py:16:6: E271 Multiple spaces after keyword 17 | #: E271 18 | a and 2 | + = help: Replace with single space + +ℹ Safe fix +13 13 | #: E271 +14 14 | a and b +15 15 | #: E271 +16 |-1 and b + 16 |+1 and b +17 17 | #: E271 +18 18 | a and 2 +19 19 | #: E271 E272 -E27.py:18:6: E271 Multiple spaces after keyword +E27.py:18:6: E271 [*] Multiple spaces after keyword | 16 | 1 and b 17 | #: E271 @@ -60,8 +115,19 @@ E27.py:18:6: E271 Multiple spaces after keyword 19 | #: E271 E272 20 | 1 and b | + = help: Replace with single space -E27.py:20:7: E271 Multiple spaces after keyword +ℹ Safe fix +15 15 | #: E271 +16 16 | 1 and b +17 17 | #: E271 +18 |-a and 2 + 18 |+a and 2 +19 19 | #: E271 E272 +20 20 | 1 and b +21 21 | #: E271 E272 + +E27.py:20:7: E271 [*] Multiple spaces after keyword | 18 | a and 2 19 | #: E271 E272 @@ -70,8 +136,19 @@ E27.py:20:7: E271 Multiple spaces after keyword 21 | #: E271 E272 22 | a and 2 | + = help: Replace with single space + +ℹ Safe fix +17 17 | #: E271 +18 18 | a and 2 +19 19 | #: E271 E272 +20 |-1 and b + 20 |+1 and b +21 21 | #: E271 E272 +22 22 | a and 2 +23 23 | #: E272 -E27.py:22:7: E271 Multiple spaces after keyword +E27.py:22:7: E271 [*] Multiple spaces after keyword | 20 | 1 and b 21 | #: E271 E272 @@ -80,8 +157,19 @@ E27.py:22:7: E271 Multiple spaces after keyword 23 | #: E272 24 | this and False | + = help: Replace with single space -E27.py:35:14: E271 Multiple spaces after keyword +ℹ Safe fix +19 19 | #: E271 E272 +20 20 | 1 and b +21 21 | #: E271 E272 +22 |-a and 2 + 22 |+a and 2 +23 23 | #: E272 +24 24 | this and False +25 25 | #: E273 + +E27.py:35:14: E271 [*] Multiple spaces after keyword | 33 | from v import c, d 34 | #: E271 @@ -90,5 +178,16 @@ E27.py:35:14: E271 Multiple spaces after keyword 36 | #: E275 37 | from w import(e, f) | + = help: Replace with single space + +ℹ Safe fix +32 32 | from u import (a, b) +33 33 | from v import c, d +34 34 | #: E271 +35 |-from w import (e, f) + 35 |+from w import (e, f) +36 36 | #: E275 +37 37 | from w import(e, f) +38 38 | #: E275 diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E272_E27.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E272_E27.py.snap index de8e255e62502..873c82eab8dcc 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E272_E27.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E272_E27.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E27.py:20:2: E272 Multiple spaces before keyword +E27.py:20:2: E272 [*] Multiple spaces before keyword | 18 | a and 2 19 | #: E271 E272 @@ -10,8 +10,19 @@ E27.py:20:2: E272 Multiple spaces before keyword 21 | #: E271 E272 22 | a and 2 | + = help: Replace with single space -E27.py:22:2: E272 Multiple spaces before keyword +ℹ Safe fix +17 17 | #: E271 +18 18 | a and 2 +19 19 | #: E271 E272 +20 |-1 and b + 20 |+1 and b +21 21 | #: E271 E272 +22 22 | a and 2 +23 23 | #: E272 + +E27.py:22:2: E272 [*] Multiple spaces before keyword | 20 | 1 and b 21 | #: E271 E272 @@ -20,8 +31,19 @@ E27.py:22:2: E272 Multiple spaces before keyword 23 | #: E272 24 | this and False | + = help: Replace with single space + +ℹ Safe fix +19 19 | #: E271 E272 +20 20 | 1 and b +21 21 | #: E271 E272 +22 |-a and 2 + 22 |+a and 2 +23 23 | #: E272 +24 24 | this and False +25 25 | #: E273 -E27.py:24:5: E272 Multiple spaces before keyword +E27.py:24:5: E272 [*] Multiple spaces before keyword | 22 | a and 2 23 | #: E272 @@ -30,5 +52,16 @@ E27.py:24:5: E272 Multiple spaces before keyword 25 | #: E273 26 | a and b | + = help: Replace with single space + +ℹ Safe fix +21 21 | #: E271 E272 +22 22 | a and 2 +23 23 | #: E272 +24 |-this and False + 24 |+this and False +25 25 | #: E273 +26 26 | a and b +27 27 | #: E274