Skip to content

Commit

Permalink
Implement pycodestyle whitespace lints W291, W293
Browse files Browse the repository at this point in the history
  • Loading branch information
mknaw committed Feb 22, 2023
1 parent 9645790 commit 31d39c1
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 2 deletions.
10 changes: 10 additions & 0 deletions crates/ruff/resources/test/fixtures/pycodestyle/W291.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
def first_func():
# The line below has two spaces after its final character
pass

# The line below has only spaces, but that's for W293

# Don't want trailing tabs either
x = 1000
# Nor a mix of tabs and spaces
y = 100
10 changes: 10 additions & 0 deletions crates/ruff/resources/test/fixtures/pycodestyle/W293.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
def first_func():
# The line below has two spaces after its final character, but that's for W291
pass

# The line below has only spaces

# Don't want trailing tabs either, but that's for W291
x = 1000
# Nor a mix of tabs and spaces, but that's for W291
y = 100
18 changes: 18 additions & 0 deletions crates/ruff/src/checkers/physical_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::rules::flake8_executable::rules::{
};
use crate::rules::pycodestyle::rules::{
doc_line_too_long, line_too_long, mixed_spaces_and_tabs, no_newline_at_end_of_file,
trailing_whitespace,
};
use crate::rules::pygrep_hooks::rules::{blanket_noqa, blanket_type_ignore};
use crate::rules::pylint;
Expand Down Expand Up @@ -41,6 +42,8 @@ pub fn check_physical_lines(
let enforce_unnecessary_coding_comment = settings.rules.enabled(&Rule::UTF8EncodingDeclaration);
let enforce_mixed_spaces_and_tabs = settings.rules.enabled(&Rule::MixedSpacesAndTabs);
let enforce_bidirectional_unicode = settings.rules.enabled(&Rule::BidirectionalUnicode);
let enforce_trailing_whitespace = settings.rules.enabled(&Rule::TrailingWhitespace);
let enforce_blank_line_contains_whitespace = settings.rules.enabled(&Rule::BlankLineContainsWhitespace);

let fix_unnecessary_coding_comment = matches!(autofix, flags::Autofix::Enabled)
&& settings.rules.should_fix(&Rule::UTF8EncodingDeclaration);
Expand Down Expand Up @@ -139,6 +142,21 @@ pub fn check_physical_lines(
if enforce_bidirectional_unicode {
diagnostics.extend(pylint::rules::bidirectional_unicode(index, line));
}

if enforce_trailing_whitespace || enforce_blank_line_contains_whitespace {
if let Some(diagnostic) = trailing_whitespace(
index,
line,
enforce_trailing_whitespace,
matches!(autofix, flags::Autofix::Enabled)
&& settings.rules.should_fix(&Rule::TrailingWhitespace),
enforce_blank_line_contains_whitespace,
matches!(autofix, flags::Autofix::Enabled)
&& settings.rules.should_fix(&Rule::BlankLineContainsWhitespace),
) {
diagnostics.push(diagnostic);
}
}
}

if enforce_no_newline_at_end_of_file {
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
(Pycodestyle, "E999") => Rule::SyntaxError,

// pycodestyle warnings
(Pycodestyle, "W291") => Rule::TrailingWhitespace,
(Pycodestyle, "W292") => Rule::NoNewLineAtEndOfFile,
(Pycodestyle, "W293") => Rule::BlankLineContainsWhitespace,
(Pycodestyle, "W505") => Rule::DocLineTooLong,
(Pycodestyle, "W605") => Rule::InvalidEscapeSequence,

Expand Down
6 changes: 5 additions & 1 deletion crates/ruff/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ ruff_macros::register_rules!(
rules::pycodestyle::rules::IOError,
rules::pycodestyle::rules::SyntaxError,
// pycodestyle warnings
rules::pycodestyle::rules::TrailingWhitespace,
rules::pycodestyle::rules::NoNewLineAtEndOfFile,
rules::pycodestyle::rules::BlankLineContainsWhitespace,
rules::pycodestyle::rules::DocLineTooLong,
rules::pycodestyle::rules::InvalidEscapeSequence,
// pyflakes
Expand Down Expand Up @@ -782,7 +784,9 @@ impl Rule {
| Rule::ShebangNewline
| Rule::BidirectionalUnicode
| Rule::ShebangPython
| Rule::ShebangWhitespace => &LintSource::PhysicalLines,
| Rule::ShebangWhitespace
| Rule::TrailingWhitespace
| Rule::BlankLineContainsWhitespace => &LintSource::PhysicalLines,
Rule::AmbiguousUnicodeCharacterComment
| Rule::AmbiguousUnicodeCharacterDocstring
| Rule::AmbiguousUnicodeCharacterString
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff/src/rules/pycodestyle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mod tests {
#[test_case(Rule::AmbiguousVariableName, Path::new("E741.py"))]
#[test_case(Rule::LambdaAssignment, Path::new("E731.py"))]
#[test_case(Rule::BareExcept, Path::new("E722.py"))]
#[test_case(Rule::BlankLineContainsWhitespace, Path::new("W293.py"))]
#[test_case(Rule::InvalidEscapeSequence, Path::new("W605_0.py"))]
#[test_case(Rule::InvalidEscapeSequence, Path::new("W605_1.py"))]
#[test_case(Rule::LineTooLong, Path::new("E501.py"))]
Expand All @@ -41,6 +42,7 @@ mod tests {
#[test_case(Rule::NotInTest, Path::new("E713.py"))]
#[test_case(Rule::NotIsTest, Path::new("E714.py"))]
#[test_case(Rule::SyntaxError, Path::new("E999.py"))]
#[test_case(Rule::TrailingWhitespace, Path::new("W291.py"))]
#[test_case(Rule::TrueFalseComparison, Path::new("E712.py"))]
#[test_case(Rule::TypeComparison, Path::new("E721.py"))]
#[test_case(Rule::UselessSemicolon, Path::new("E70.py"))]
Expand Down
4 changes: 4 additions & 0 deletions crates/ruff/src/rules/pycodestyle/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ pub use space_around_operator::{
space_around_operator, MultipleSpacesAfterOperator, MultipleSpacesBeforeOperator,
TabAfterOperator, TabBeforeOperator,
};
pub use trailing_whitespace::{
trailing_whitespace, BlankLineContainsWhitespace, TrailingWhitespace,
};
pub use type_comparison::{type_comparison, TypeComparison};
pub use whitespace_around_keywords::{
whitespace_around_keywords, MultipleSpacesAfterKeyword, MultipleSpacesBeforeKeyword,
Expand Down Expand Up @@ -60,6 +63,7 @@ mod mixed_spaces_and_tabs;
mod no_newline_at_end_of_file;
mod not_tests;
mod space_around_operator;
mod trailing_whitespace;
mod type_comparison;
mod whitespace_around_keywords;
mod whitespace_before_comment;
69 changes: 69 additions & 0 deletions crates/ruff/src/rules/pycodestyle/rules/trailing_whitespace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use ruff_macros::{define_violation, derive_message_formats};
use rustpython_parser::ast::Location;

use crate::ast::types::Range;
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::violation::AlwaysAutofixableViolation;

define_violation!(
pub struct TrailingWhitespace;
);
impl AlwaysAutofixableViolation for TrailingWhitespace {
#[derive_message_formats]
fn message(&self) -> String {
format!("Trailing whitespace")
}

fn autofix_title(&self) -> String {
"Remove trailing whitespace".to_string()
}
}

define_violation!(
pub struct BlankLineContainsWhitespace;
);
impl AlwaysAutofixableViolation for BlankLineContainsWhitespace {
#[derive_message_formats]
fn message(&self) -> String {
format!("Blank line contains whitespace")
}

fn autofix_title(&self) -> String {
"Remove whitespace from blank line".to_string()
}
}

/// W291 & W293
pub fn trailing_whitespace(
lineno: usize,
line: &str,
enforce_trailing_whitespace: bool,
fix_trailing_whitespace: bool,
enforce_blank_line_contains_whitespace: bool,
fix_blank_line_contains_whitespace: bool,
) -> Option<Diagnostic> {
let whitespace_count = line.chars().rev().take_while(|c| c.is_whitespace()).count();
if whitespace_count > 0 {
let start = Location::new(lineno + 1, line.len() - whitespace_count);
let end = Location::new(lineno + 1, line.len());

if whitespace_count == line.len() {
if enforce_blank_line_contains_whitespace {
let mut diagnostic =
Diagnostic::new(BlankLineContainsWhitespace, Range::new(start, end));
if fix_blank_line_contains_whitespace {
diagnostic.amend(Fix::deletion(start, end));
}
return Some(diagnostic);
}
} else if enforce_trailing_whitespace {
let mut diagnostic = Diagnostic::new(TrailingWhitespace, Range::new(start, end));
if fix_trailing_whitespace {
diagnostic.amend(Fix::deletion(start, end));
}
return Some(diagnostic);
}
}
None
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
source: crates/ruff/src/rules/pycodestyle/mod.rs
expression: diagnostics
---
- kind:
TrailingWhitespace: ~
location:
row: 3
column: 8
end_location:
row: 3
column: 10
fix:
content: ""
location:
row: 3
column: 8
end_location:
row: 3
column: 10
parent: ~
- kind:
TrailingWhitespace: ~
location:
row: 8
column: 8
end_location:
row: 8
column: 9
fix:
content: ""
location:
row: 8
column: 8
end_location:
row: 8
column: 9
parent: ~
- kind:
TrailingWhitespace: ~
location:
row: 10
column: 7
end_location:
row: 10
column: 9
fix:
content: ""
location:
row: 10
column: 7
end_location:
row: 10
column: 9
parent: ~

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: crates/ruff/src/rules/pycodestyle/mod.rs
expression: diagnostics
---
- kind:
BlankLineContainsWhitespace: ~
location:
row: 6
column: 0
end_location:
row: 6
column: 4
fix:
content: ""
location:
row: 6
column: 0
end_location:
row: 6
column: 4
parent: ~

4 changes: 3 additions & 1 deletion ruff.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 31d39c1

Please sign in to comment.