From dbc0825d7ac729806764c5f1ffa1845cbb887a9e Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 11 Mar 2024 21:34:40 -0400 Subject: [PATCH] Tweak message; remove locator --- crates/ruff_linter/src/checkers/tokens.rs | 2 +- .../rules/missing_newline_at_end_of_file.rs | 2 +- .../rules/too_many_newlines_at_end_of_file.rs | 76 ++++++++++++------- ...style__tests__preview__W391_W391_0.py.snap | 4 +- ...style__tests__preview__W391_W391_2.py.snap | 4 +- 5 files changed, 53 insertions(+), 35 deletions(-) diff --git a/crates/ruff_linter/src/checkers/tokens.rs b/crates/ruff_linter/src/checkers/tokens.rs index e3456e7149249d..fa801a3284c742 100644 --- a/crates/ruff_linter/src/checkers/tokens.rs +++ b/crates/ruff_linter/src/checkers/tokens.rs @@ -204,7 +204,7 @@ pub(crate) fn check_tokens( } if settings.rules.enabled(Rule::TooManyNewlinesAtEndOfFile) { - pycodestyle::rules::too_many_newlines_at_end_of_file(&mut diagnostics, tokens, locator); + pycodestyle::rules::too_many_newlines_at_end_of_file(&mut diagnostics, tokens); } diagnostics.retain(|diagnostic| settings.rules.enabled(diagnostic.kind.rule())); diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/missing_newline_at_end_of_file.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/missing_newline_at_end_of_file.rs index 4713d0f35fd5ac..f7ee6ae4901231 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/missing_newline_at_end_of_file.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/missing_newline_at_end_of_file.rs @@ -42,7 +42,7 @@ pub(crate) fn no_newline_at_end_of_file( ) -> Option { let source = locator.contents(); - // Ignore empty and BOM only files + // Ignore empty and BOM only files. if source.is_empty() || source == "\u{feff}" { return None; } diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/too_many_newlines_at_end_of_file.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/too_many_newlines_at_end_of_file.rs index 9f58066c180c52..f39e90049b51ca 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/too_many_newlines_at_end_of_file.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/too_many_newlines_at_end_of_file.rs @@ -2,15 +2,15 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_parser::lexer::LexResult; use ruff_python_parser::Tok; -use ruff_source_file::Locator; use ruff_text_size::{TextRange, TextSize}; /// ## What it does -/// Checks for files with too many new lines at the end of the file. +/// Checks for files with multiple trailing blank lines. /// /// ## Why is this bad? -/// Trailing blank lines are superfluous. -/// However the last line should end with a new line. +/// Trailing blank lines in a file are superfluous. +/// +/// However, the last line of the file should end with a newline. /// /// ## Example /// ```python @@ -22,16 +22,35 @@ use ruff_text_size::{TextRange, TextSize}; /// spam(1)\n /// ``` #[violation] -pub struct TooManyNewlinesAtEndOfFile; +pub struct TooManyNewlinesAtEndOfFile { + num_trailing_newlines: u32, +} impl AlwaysFixableViolation for TooManyNewlinesAtEndOfFile { #[derive_message_formats] fn message(&self) -> String { - format!("Too many newlines at the end of file") + let TooManyNewlinesAtEndOfFile { + num_trailing_newlines, + } = self; + + // We expect a single trailing newline; so two trailing newlines is one too many, three + // trailing newlines is two too many, etc. + if *num_trailing_newlines > 2 { + format!("Too many newlines at end of file") + } else { + format!("Extra newline at end of file") + } } fn fix_title(&self) -> String { - "Remove extraneous trailing newlines".to_string() + let TooManyNewlinesAtEndOfFile { + num_trailing_newlines, + } = self; + if *num_trailing_newlines > 2 { + "Remove trailing newlines".to_string() + } else { + "Remove trailing newline".to_string() + } } } @@ -39,27 +58,20 @@ impl AlwaysFixableViolation for TooManyNewlinesAtEndOfFile { pub(crate) fn too_many_newlines_at_end_of_file( diagnostics: &mut Vec, lxr: &[LexResult], - locator: &Locator, ) { - let source = locator.contents(); - - // Ignore empty and BOM only files - if source.is_empty() || source == "\u{feff}" { - return; - } - - let mut count = 0; - let mut start_pos: Option = None; - let mut end_pos: Option = None; + let mut num_trailing_newlines = 0u32; + let mut start: Option = None; + let mut end: Option = None; - for &(ref tok, range) in lxr.iter().rev().flatten() { + // Count the number of trailing newlines. + for (tok, range) in lxr.iter().rev().flatten() { match tok { Tok::NonLogicalNewline | Tok::Newline => { - if count == 0 { - end_pos = Some(range.end()); + if num_trailing_newlines == 0 { + end = Some(range.end()); } - start_pos = Some(range.end()); - count += 1; + start = Some(range.end()); + num_trailing_newlines += 1; } Tok::Dedent => continue, _ => { @@ -68,12 +80,18 @@ pub(crate) fn too_many_newlines_at_end_of_file( } } - if count > 1 { - let start = start_pos.unwrap(); - let end = end_pos.unwrap(); - let range = TextRange::new(start, end); - let mut diagnostic = Diagnostic::new(TooManyNewlinesAtEndOfFile, range); - diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); + if num_trailing_newlines > 1 { + let range = match (start, end) { + (Some(start), Some(end)) => TextRange::new(start, end), + _ => return, + }; + let mut diagnostic = Diagnostic::new( + TooManyNewlinesAtEndOfFile { + num_trailing_newlines, + }, + range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(range))); diagnostics.push(diagnostic); } } diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__W391_W391_0.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__W391_W391_0.py.snap index 21c2b4ad04a9e0..643743f66be969 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__W391_W391_0.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__W391_W391_0.py.snap @@ -1,14 +1,14 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -W391_0.py:14:1: W391 [*] Too many newlines at the end of file +W391_0.py:14:1: W391 [*] Extra newline at end of file | 12 | foo() 13 | bar() 14 | | ^ W391 | - = help: Remove extraneous trailing newlines + = help: Remove trailing newline ℹ Safe fix 11 11 | if __name__ == '__main__': diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__W391_W391_2.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__W391_W391_2.py.snap index fa84d6ad4f412b..8ca9ecd0c14586 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__W391_W391_2.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__W391_W391_2.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -W391_2.py:14:1: W391 [*] Too many newlines at the end of file +W391_2.py:14:1: W391 [*] Too many newlines at end of file | 12 | foo() 13 | bar() @@ -10,7 +10,7 @@ W391_2.py:14:1: W391 [*] Too many newlines at the end of file 16 | | 17 | | | - = help: Remove extraneous trailing newlines + = help: Remove trailing newlines ℹ Safe fix 11 11 | if __name__ == '__main__':