From 32ec9e7666d2bc27af193e174038e5c80f082b46 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 10 Nov 2023 00:36:24 +0900 Subject: [PATCH 001/122] Add V1 version. --- crates/flake8_to_ruff/src/parser.rs | 4 + .../test/fixtures/pycodestyle/E30.py | 567 ++++++++++++++++++ .../ruff_linter/src/checkers/logical_lines.rs | 20 +- crates/ruff_linter/src/codes.rs | 6 + crates/ruff_linter/src/registry.rs | 9 +- .../ruff_linter/src/rules/pycodestyle/mod.rs | 25 + .../rules/logical_lines/blank_lines.rs | 479 +++++++++++++++ .../pycodestyle/rules/logical_lines/mod.rs | 19 +- ruff.schema.json | 8 +- scripts/check_docs_formatted.py | 6 + 10 files changed, 1135 insertions(+), 8 deletions(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py create mode 100644 crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs diff --git a/crates/flake8_to_ruff/src/parser.rs b/crates/flake8_to_ruff/src/parser.rs index 63ab9b68008b7..8a457dc40af5f 100644 --- a/crates/flake8_to_ruff/src/parser.rs +++ b/crates/flake8_to_ruff/src/parser.rs @@ -289,6 +289,10 @@ mod tests { pattern: "examples/*".to_string(), prefix: codes::Pyflakes::_841.into(), }, + PatternPrefixPair { + pattern: "*.pyi".to_string(), + prefix: codes::Pycodestyle::E302.into(), + }, ]; assert_eq!(actual, expected); diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py new file mode 100644 index 0000000000000..6ef70a60b35a3 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -0,0 +1,567 @@ +"""Fixtures for the errors E301, E302, E303, E304, E305 and E306. +Since these errors are about new lines, each test starts with either "No error" or "# E30X". +Each test's end is signaled by a "# end" line. +There should be no E30X error outside of a test's bound. +""" + + +# No error +class Class: + pass +# end + + +# No error +def func(): + pass +# end + + +# No error +# comment +class Class: + pass +# end + + +# No error +# comment +def func(): + pass +# end + + +# no error +def foo(): + pass + + +def bar(): + pass + + +class Foo(object): + pass + + +class Bar(object): + pass +# end + + +# No error +class Class(object): + + def func1(): + pass + + def func2(): + pass +# end + + +# No error +class Class: + + def func1(): + pass + + # comment + def func2(): + pass + + # This is a + # ... multi-line comment + + def func3(): + pass + + +# This is a +# ... multi-line comment + +@decorator +class Class: + + def func1(): + pass + + # comment + + def func2(): + pass + + @property + def func3(): + pass + +# end + + +# No error +try: + from nonexistent import Bar +except ImportError: + class Bar(object): + """This is a Bar replacement""" +# end + + +# No error +def with_feature(f): + """Some decorator""" + wrapper = f + if has_this_feature(f): + def wrapper(*args): + call_feature(args[0]) + return f(*args) + return wrapper +# end + + +# No error +try: + next +except NameError: + def next(iterator, default): + for item in iterator: + return item + return default +# end + + +# No error +def fn(): + pass + + +class Foo(): + """Class Foo""" + + def fn(): + + pass +# end + + +# No error +# comment +def c(): + pass + + +# comment + + +def d(): + pass + +# This is a +# ... multi-line comment + +# And this one is +# ... a second paragraph +# ... which spans on 3 lines + + +# Function `e` is below +# NOTE: Hey this is a testcase + +def e(): + pass + + +def fn(): + print() + + # comment + + print() + + print() + +# Comment 1 + +# Comment 2 + + +# Comment 3 + +def fn2(): + + pass +# end + + +# no error +if __name__ == '__main__': + foo() +# end + + +# no error +defaults = {} +defaults.update({}) +# end + + +# no error +def foo(x): + classification = x + definitely = not classification +# end + + +# no error +def bar(): pass +def baz(): pass +# end + + +# no error +def foo(): + def bar(): pass + def baz(): pass +# end + + +# no error +from typing import overload +from typing import Union +# end + + +# no error +@overload +def f(x: int) -> int: ... +@overload +def f(x: str) -> str: ... +# end + + +# no error +def f(x: Union[int, str]) -> Union[int, str]: + return x +# end + + +# no error +from typing import Protocol + + +class C(Protocol): + @property + def f(self) -> int: ... + @property + def g(self) -> str: ... +# end + + +# no error +def f( + a, +): + pass +# end + + +# E301 +class Class(object): + + def func1(): + pass + def func2(): + pass +# end + + +# E301 +class Class: + + def fn1(): + pass + # comment + def fn2(): + pass +# end + + +# E302 +"""Main module.""" +def fn(): + pass +# end + + +# E302 +import sys +def get_sys_path(): + return sys.path +# end + + +# E302 +def a(): + pass + +def b(): + pass +# end + + +# E302 +def a(): + pass + +# comment + +def b(): + pass +# end + + +# E302 +def a(): + pass + +async def b(): + pass +# end + + +# E302 +async def x(): + pass + +async def x(y: int = 1): + pass +# end + + +# E302 +def bar(): + pass +def baz(): pass +# end + + +# E302 +def bar(): pass +def baz(): + pass +# end + + +# E302 +def f(): + pass + +# comment +@decorator +def g(): + pass +# end + + +# E303 +def fn(): + _ = None + + + # arbitrary comment + + def inner(): # E306 not expected + pass +# end + + +# E303 +def fn(): + _ = None + + + # arbitrary comment + def inner(): # E306 not expected + pass +# end + + +# E303 +print() + + + +print() +# end + + +# E303:5:1 +print() + + + +# comment + +print() +# end + + +# E303:5:5 E303:8:5 +def a(): + print() + + + # comment + + + # another comment + + print() +# end + + +# E303 +#!python + + + +"""This class docstring comes on line 5. +It gives error E303: too many blank lines (3) +""" +# end + + +# E304 +@decorator + +def function(): + pass +# end + + +# E305:7:1 +def fn(): + print() + + # comment + + # another comment +fn() +# end + + +# E305 +class Class(): + pass + + # comment + + # another comment +a = 1 +# end + + +# E305:8:1 +def fn(): + print() + + # comment + + # another comment + +try: + fn() +except Exception: + pass +# end + + +# E305:5:1 +def a(): + print + +# Two spaces before comments, too. +if a(): + a() +# end + + +#: E305:8:1 +# Example from https://github.com/PyCQA/pycodestyle/issues/400 +import stuff + + +def main(): + blah, blah + +if __name__ == '__main__': + main() +# end + + +# E306:3:5 +def a(): + x = 1 + def b(): + pass +# end + + +#: E306:3:5 +async def a(): + x = 1 + def b(): + pass +# end + + +#: E306:3:5 E306:5:9 +def a(): + x = 2 + def b(): + x = 1 + def c(): + pass +# end + + +# E306:3:5 E306:6:5 +def a(): + x = 1 + class C: + pass + x = 2 + def b(): + pass +# end + + +# E306:4:5 +def foo(): + def bar(): + pass + def baz(): pass +# end + + +# E306:3:5 +def foo(): + def bar(): pass + def baz(): + pass +# end + + +# E306 +class C: + def f(): + pass +# end + + +# E306 +def f(): + def f(): + pass +# end + diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index c216ce2f671a6..07c77fae1c3b9 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -7,10 +7,11 @@ use ruff_text_size::{Ranged, TextRange}; use crate::registry::AsRule; use crate::rules::pycodestyle::rules::logical_lines::{ - extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword, - missing_whitespace_around_operator, space_after_comma, space_around_operator, - whitespace_around_keywords, whitespace_around_named_parameter_equals, - whitespace_before_comment, whitespace_before_parameters, LogicalLines, TokenFlags, + blank_lines, extraneous_whitespace, indentation, missing_whitespace, + missing_whitespace_after_keyword, missing_whitespace_around_operator, space_after_comma, + space_around_operator, whitespace_around_keywords, whitespace_around_named_parameter_equals, + whitespace_before_comment, whitespace_before_parameters, BlankLinesTrackingVars, LogicalLines, + TokenFlags, }; use crate::settings::LinterSettings; @@ -38,6 +39,7 @@ pub(crate) fn check_logical_lines( ) -> Vec { let mut context = LogicalLinesContext::new(settings); + let mut blank_lines_tracking_vars = BlankLinesTrackingVars::default(); let mut prev_line = None; let mut prev_indent_level = None; let indent_char = stylist.indentation().as_char(); @@ -101,6 +103,16 @@ pub(crate) fn check_logical_lines( } } + blank_lines( + &line, + prev_line.as_ref(), + &mut blank_lines_tracking_vars, + indent_level, + locator, + stylist, + &mut context, + ); + if !line.is_comment_only() { prev_line = Some(line); prev_indent_level = Some(indent_level); diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 1c836a092be94..cfd9b938b65c2 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -132,6 +132,12 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pycodestyle, "E274") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TabBeforeKeyword), #[allow(deprecated)] (Pycodestyle, "E275") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAfterKeyword), + (Pycodestyle, "E301") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLineBetweenMethods), + (Pycodestyle, "E302") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLinesTopLevel), + (Pycodestyle, "E303") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TooManyBlankLines), + (Pycodestyle, "E304") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLineAfterDecorator), + (Pycodestyle, "E305") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLinesAfterFunctionOrClass), + (Pycodestyle, "E306") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLinesBeforeNestedDefinition), (Pycodestyle, "E401") => (RuleGroup::Stable, rules::pycodestyle::rules::MultipleImportsOnOneLine), (Pycodestyle, "E402") => (RuleGroup::Stable, rules::pycodestyle::rules::ModuleImportNotAtTopOfFile), (Pycodestyle, "E501") => (RuleGroup::Stable, rules::pycodestyle::rules::LineTooLong), diff --git a/crates/ruff_linter/src/registry.rs b/crates/ruff_linter/src/registry.rs index 7bd5ba4faeedb..c92e36fbfc2a0 100644 --- a/crates/ruff_linter/src/registry.rs +++ b/crates/ruff_linter/src/registry.rs @@ -299,7 +299,13 @@ impl Rule { Rule::IOError => LintSource::Io, Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports, Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => LintSource::Filesystem, - Rule::IndentationWithInvalidMultiple + Rule::BlankLineAfterDecorator + | Rule::BlankLineBetweenMethods + | Rule::BlankLinesAfterFunctionOrClass + | Rule::BlankLinesBeforeNestedDefinition + | Rule::BlankLinesTopLevel + | Rule::IndentationWithInvalidMultiple + | Rule::IndentationWithInvalidMultiple | Rule::IndentationWithInvalidMultipleComment | Rule::MissingWhitespace | Rule::MissingWhitespaceAfterKeyword @@ -325,6 +331,7 @@ impl Rule { | Rule::TabBeforeKeyword | Rule::TabBeforeOperator | Rule::TooFewSpacesBeforeInlineComment + | Rule::TooManyBlankLines | Rule::UnexpectedIndentation | Rule::UnexpectedIndentationComment | Rule::UnexpectedSpacesAroundKeywordParameterEquals diff --git a/crates/ruff_linter/src/rules/pycodestyle/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/mod.rs index d91a46ff81fcb..ace20dd42761b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/mod.rs @@ -122,6 +122,31 @@ mod tests { #[test_case(Rule::TooFewSpacesBeforeInlineComment, Path::new("E26.py"))] #[test_case(Rule::UnexpectedIndentation, Path::new("E11.py"))] #[test_case(Rule::UnexpectedIndentationComment, Path::new("E11.py"))] + #[test_case(Rule::UnderIndentedHangingIndent, Path::new("E12.py"))] + #[test_case(Rule::MissingOrOutdentedIndentation, Path::new("E12.py"))] + #[test_case( + Rule::ClosingBracketNotMatchingOpeningBracketIndentation, + Path::new("E12.py") + )] + #[test_case( + Rule::ClosingBracketNotMatchingOpeningBracketVisualIndentation, + Path::new("E12.py") + )] + #[test_case(Rule::ContinuationLineIndentSameAsNextLogicalLine, Path::new("E12.py"))] + #[test_case( + Rule::ContinuationLineOverIndentedForHangingIndent, + Path::new("E12.py") + )] + #[test_case(Rule::ContinuationLineOverIndentedForVisualIndent, Path::new("E12.py"))] + #[test_case( + Rule::ContinuationLineUnderIndentedForVisualIndent, + Path::new("E12.py") + )] + #[test_case( + Rule::VisuallyIndentedLineWithSameIndentAsNextLogicalLine, + Path::new("E12.py") + )] + #[test_case(Rule::ContinuationLineUnalignedForHangingIndent, Path::new("E12.py"))] #[test_case(Rule::WhitespaceAfterOpenBracket, Path::new("E20.py"))] #[test_case(Rule::WhitespaceBeforeCloseBracket, Path::new("E20.py"))] #[test_case(Rule::WhitespaceBeforePunctuation, Path::new("E20.py"))] diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs new file mode 100644 index 0000000000000..0bfc0a162bc38 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -0,0 +1,479 @@ +use ruff_diagnostics::AlwaysFixableViolation; +use ruff_diagnostics::Diagnostic; +use ruff_diagnostics::Edit; +use ruff_diagnostics::Fix; + +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_codegen::Stylist; +use ruff_python_parser::TokenKind; +use ruff_source_file::Locator; +use ruff_text_size::TextSize; + +use crate::checkers::logical_lines::LogicalLinesContext; + +use super::LogicalLine; + +/// Contains variables used for the linting of blank lines. +#[derive(Default)] +#[allow(clippy::struct_excessive_bools)] +pub(crate) struct BlankLinesTrackingVars { + follows_decorator: bool, + follows_def: bool, + is_in_class: bool, + /// The indent level where the class started. + class_indent_level: usize, + is_in_fn: bool, + /// The indent level where the function started. + fn_indent_level: usize, +} + +/// Number of blank lines between various code parts. +struct BlankLinesConfig; + +impl BlankLinesConfig { + /// Number of blank lines around top level classes and functions. + const TOP_LEVEL: u32 = 2; + /// Number of blank lines around methods and nested classes and functions. + const METHOD: u32 = 1; +} + +/// ## What it does +/// Checks for missing blank lines between methods of a class. +/// +/// ## Why is this bad? +/// PEP 8 recommends the use of blank lines as follows: +/// - Two blank lines are expected between functions and classes +/// - One blank line is expected between methods of a class. +/// +/// ## Example +/// ```python +/// class MyClass(object): +/// def func1(): +/// pass +/// def func2(): +/// pass +/// ``` +/// +/// Use instead: +/// ```python +/// class MyClass(object): +/// def func1(): +/// pass +/// +/// def func2(): +/// pass +/// ``` +/// +/// ## References +/// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) +/// - [Flake 8 rule](https://www.flake8rules.com/rules/E301.html) +#[violation] +pub struct BlankLineBetweenMethods(pub u32); + +impl AlwaysFixableViolation for BlankLineBetweenMethods { + #[derive_message_formats] + fn message(&self) -> String { + let BlankLineBetweenMethods(nb_blank_lines) = self; + format!( + "Expected {:?} blank line, found {nb_blank_lines}", + BlankLinesConfig::METHOD + ) + } + + fn fix_title(&self) -> String { + "Add missing blank line(s)".to_string() + } +} + +/// ## What it does +/// Checks for missing blank lines between top level functions and classes. +/// +/// ## Why is this bad? +/// PEP 8 recommends the use of blank lines as follows: +/// - Two blank lines are expected between functions and classes +/// - One blank line is expected between methods of a class. +/// +/// ## Example +/// ```python +/// def func1(): +/// pass +/// def func2(): +/// pass +/// ``` +/// +/// Use instead: +/// ```python +/// def func1(): +/// pass +/// +/// +/// def func2(): +/// pass +/// ``` +/// +/// ## References +/// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) +/// - [Flake 8 rule](https://www.flake8rules.com/rules/E302.html) +#[violation] +pub struct BlankLinesTopLevel(pub u32); + +impl AlwaysFixableViolation for BlankLinesTopLevel { + #[derive_message_formats] + fn message(&self) -> String { + let BlankLinesTopLevel(nb_blank_lines) = self; + format!( + "Expected {:?} blank lines, found {nb_blank_lines}", + BlankLinesConfig::TOP_LEVEL + ) + } + + fn fix_title(&self) -> String { + "Add missing blank line(s)".to_string() + } +} + +/// ## What it does +/// Checks for extraneous blank lines. +/// +/// ## Why is this bad? +/// PEP 8 recommends the using blank lines as following: +/// - Two blank lines are expected between functions and classes +/// - One blank line is expected between methods of a class. +/// +/// ## Example +/// ```python +/// def func1(): +/// pass +/// +/// +/// +/// def func2(): +/// pass +/// ``` +/// +/// Use instead: +/// ```python +/// def func1(): +/// pass +/// +/// +/// def func2(): +/// pass +/// ``` +/// +/// ## References +/// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) +/// - [Flake 8 rule](https://www.flake8rules.com/rules/E303.html) +#[violation] +pub struct TooManyBlankLines(pub u32); + +impl AlwaysFixableViolation for TooManyBlankLines { + #[derive_message_formats] + fn message(&self) -> String { + let TooManyBlankLines(nb_blank_lines) = self; + format!("Too many blank lines ({nb_blank_lines})") + } + + fn fix_title(&self) -> String { + "Remove extraneous blank line(s)".to_string() + } +} + +/// ## What it does +/// Checks for missing blank line after function decorator. +/// +/// ## Why is this bad? +/// PEP 8 recommends the use of blank lines as follows: +/// - Two blank lines are expected between functions and classes +/// - One blank line is expected between methods of a class. +/// +/// ## Example +/// ```python +/// class User(object): +/// +/// @property +/// +/// def name(self): +/// pass +/// ``` +/// +/// Use instead: +/// ```python +/// class User(object): +/// +/// @property +/// def name(self): +/// pass +/// ``` +/// +/// ## References +/// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) +/// - [Flake 8 rule](https://www.flake8rules.com/rules/E304.html) +#[violation] +pub struct BlankLineAfterDecorator; + +impl AlwaysFixableViolation for BlankLineAfterDecorator { + #[derive_message_formats] + fn message(&self) -> String { + format!("blank lines found after function decorator") + } + + fn fix_title(&self) -> String { + "Remove extraneous blank line(s)".to_string() + } +} + +/// ## What it does +/// Checks for missing blank lines after end of function or class. +/// +/// ## Why is this bad? +/// PEP 8 recommends the using blank lines as following: +/// - Two blank lines are expected between functions and classes +/// - One blank line is expected between methods of a class. +/// +/// ## Example +/// ```python +/// class User(object): +/// pass +/// user = User() +/// ``` +/// +/// Use instead: +/// ```python +/// class User(object): +/// pass +/// +/// +/// user = User() +/// ``` +/// +/// ## References +/// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) +/// - [Flake 8 rule](https://www.flake8rules.com/rules/E305.html) +#[violation] +pub struct BlankLinesAfterFunctionOrClass(pub u32); + +impl AlwaysFixableViolation for BlankLinesAfterFunctionOrClass { + #[derive_message_formats] + fn message(&self) -> String { + let BlankLinesAfterFunctionOrClass(blank_lines) = self; + format!("expected 2 blank lines after class or function definition, found ({blank_lines})") + } + + fn fix_title(&self) -> String { + "Add missing blank line(s)".to_string() + } +} + +/// ## What it does +/// Checks for for 1 blank line between nested functions/classes definitions. +/// +/// ## Why is this bad? +/// PEP 8 recommends the using blank lines as following: +/// - Two blank lines are expected between functions and classes +/// - One blank line is expected between methods of a class. +/// +/// ## Example +/// ```python +/// def outer(): +/// def inner(): +/// pass +/// def inner2(): +/// pass +/// ``` +/// +/// Use instead: +/// ```python +/// def outer(): +/// +/// def inner(): +/// pass +/// +/// def inner2(): +/// pass +/// ``` +/// +/// ## References +/// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) +/// - [Flake 8 rule](https://www.flake8rules.com/rules/E306.html) +#[violation] +pub struct BlankLinesBeforeNestedDefinition(pub u32); + +impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { + #[derive_message_formats] + fn message(&self) -> String { + let BlankLinesBeforeNestedDefinition(blank_lines) = self; + format!("Expected 1 blank line before a nested definition, found {blank_lines}") + } + + fn fix_title(&self) -> String { + "Add missing blank line".to_string() + } +} + +/// E301, E302, E303, E304, E305, E306 +pub(crate) fn blank_lines( + line: &LogicalLine, + prev_line: Option<&LogicalLine>, + tracked_vars: &mut BlankLinesTrackingVars, + indent_level: usize, + locator: &Locator, + stylist: &Stylist, + context: &mut LogicalLinesContext, +) { + if indent_level <= tracked_vars.class_indent_level { + tracked_vars.is_in_class = false; + } + + if indent_level <= tracked_vars.fn_indent_level { + tracked_vars.is_in_fn = false; + } + + for (token_idx, token) in line.tokens().iter().enumerate() { + if token.kind() == TokenKind::Def + && tracked_vars.is_in_class + && line.line.preceding_blank_lines == 0 + && !tracked_vars.follows_decorator + && prev_line + .and_then(|prev_line| prev_line.tokens_trimmed().first()) + .map_or(false, |token| { + !matches!(token.kind(), TokenKind::Def | TokenKind::Class) + }) + { + // E301 + let mut diagnostic = Diagnostic::new( + BlankLineBetweenMethods(line.line.preceding_blank_lines), + token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(token.range.start()), + ))); + context.push_diagnostic(diagnostic); + } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class) + && !(tracked_vars.follows_decorator + || tracked_vars.is_in_class + || tracked_vars.is_in_fn + || tracked_vars.follows_def + && line + .tokens_trimmed() + .last() + .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon))) + && prev_line + .and_then(|prev_line| prev_line.tokens_trimmed().first()) + .map_or(false, |token| !matches!(token.kind(), TokenKind::Except)) + && line.line.preceding_blank_lines < 2 + && prev_line.is_some() + { + // E302 + let mut diagnostic = Diagnostic::new( + BlankLinesTopLevel(line.line.preceding_blank_lines), + token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .as_str() + .to_string() + .repeat(2 - line.line.preceding_blank_lines as usize), + locator.line_start(token.range.start()), + ))); + context.push_diagnostic(diagnostic); + } else if token_idx == 0 + && (line.line.preceding_blank_lines > BlankLinesConfig::TOP_LEVEL + || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) + && line.line.preceding_blank_lines > BlankLinesConfig::METHOD)) + { + // E303 + let mut diagnostic = Diagnostic::new( + TooManyBlankLines(line.line.preceding_blank_lines), + token.range, + ); + + let chars_to_remove = if indent_level > 0 { + line.line.preceding_blank_characters - BlankLinesConfig::METHOD + } else { + line.line.preceding_blank_characters - BlankLinesConfig::TOP_LEVEL + }; + let end = locator.line_start(token.range.start()); + let start = end - TextSize::new(chars_to_remove); + diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); + + context.push_diagnostic(diagnostic); + } else if tracked_vars.follows_decorator && line.line.preceding_blank_lines > 0 { + // E304 + let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, token.range); + + let range = token.range; + diagnostic.set_fix(Fix::safe_edit(Edit::deletion( + locator.line_start(range.start()) + - TextSize::new(line.line.preceding_blank_characters), + locator.line_start(range.start()), + ))); + context.push_diagnostic(diagnostic); + } else if line.line.preceding_blank_lines < 2 + && (tracked_vars.is_in_fn || tracked_vars.is_in_class) + && indent_level == 0 + { + // E305 + let mut diagnostic = Diagnostic::new( + BlankLinesAfterFunctionOrClass(line.line.preceding_blank_lines), + token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .as_str() + .to_string() + .repeat(2 - line.line.preceding_blank_lines as usize), + locator.line_start(token.range.start()), + ))); + context.push_diagnostic(diagnostic); + } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class) + && (tracked_vars.is_in_class || tracked_vars.is_in_fn) + && line.line.preceding_blank_lines == 0 + { + // E306 + let mut diagnostic = Diagnostic::new( + BlankLinesBeforeNestedDefinition(line.line.preceding_blank_lines), + token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(token.range.start()), + ))); + + context.push_diagnostic(diagnostic); + } + + match token.kind() { + TokenKind::Class => { + if !tracked_vars.is_in_class { + tracked_vars.class_indent_level = indent_level; + } + tracked_vars.is_in_class = true; + tracked_vars.follows_decorator = false; + tracked_vars.follows_def = false; + break; + } + TokenKind::At => { + tracked_vars.follows_decorator = true; + tracked_vars.follows_def = false; + break; + } + TokenKind::Def => { + if !tracked_vars.is_in_fn { + tracked_vars.fn_indent_level = indent_level; + } + tracked_vars.is_in_fn = true; + tracked_vars.follows_def = true; + tracked_vars.follows_decorator = false; + break; + } + _ => { + tracked_vars.follows_decorator = false; + tracked_vars.follows_def = false; + } + } + } +} diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs index f736d6cb2fadd..80213cf340aac 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs @@ -1,3 +1,4 @@ +pub(crate) use blank_lines::*; pub(crate) use extraneous_whitespace::*; pub(crate) use indentation::*; pub(crate) use missing_whitespace::*; @@ -20,6 +21,7 @@ use ruff_python_parser::TokenKind; use ruff_python_trivia::is_python_whitespace; use ruff_source_file::Locator; +mod blank_lines; mod extraneous_whitespace; mod indentation; mod missing_whitespace; @@ -119,7 +121,7 @@ impl<'a> IntoIterator for &'a LogicalLines<'a> { /// 2 /// ] /// ``` -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) struct LogicalLine<'a> { lines: &'a LogicalLines<'a>, line: &'a Line, @@ -412,6 +414,10 @@ struct LogicalLinesBuilder { tokens: Vec, lines: Vec, current_line: CurrentLine, + /// Number of consecutive blank lines. + current_blank_lines: u32, + /// Number of blank characters in the blank lines (\n vs \r\n for example). + current_blank_characters: u32, } impl LogicalLinesBuilder { @@ -475,12 +481,19 @@ impl LogicalLinesBuilder { let is_empty = self.tokens[self.current_line.tokens_start as usize..end as usize] .iter() .all(|token| token.kind.is_newline()); - if !is_empty { + if is_empty { + self.current_blank_lines += 1; + self.current_blank_characters += end - self.current_line.tokens_start; + } else { self.lines.push(Line { flags: self.current_line.flags, + preceding_blank_lines: self.current_blank_lines, + preceding_blank_characters: self.current_blank_characters, tokens_start: self.current_line.tokens_start, tokens_end: end, }); + self.current_blank_lines = 0; + self.current_blank_characters = 0; } self.current_line = CurrentLine { @@ -504,6 +517,8 @@ impl LogicalLinesBuilder { #[derive(Debug, Clone)] struct Line { flags: TokenFlags, + preceding_blank_lines: u32, + preceding_blank_characters: u32, tokens_start: u32, tokens_end: u32, } diff --git a/ruff.schema.json b/ruff.schema.json index 2c3d3fcf1476c..57f8a9d591269 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2672,6 +2672,12 @@ "E273", "E274", "E275", + "E301", + "E302", + "E303", + "E304", + "E305", + "E306", "E4", "E40", "E401", @@ -3592,4 +3598,4 @@ "type": "string" } } -} \ No newline at end of file +} diff --git a/scripts/check_docs_formatted.py b/scripts/check_docs_formatted.py index 78334cc3b5f9a..03182133e41b9 100755 --- a/scripts/check_docs_formatted.py +++ b/scripts/check_docs_formatted.py @@ -32,6 +32,11 @@ "bad-quotes-docstring", "bad-quotes-inline-string", "bad-quotes-multiline-string", + "blank-line-after-decorator", + "blank-line-between-methods", + "blank-lines-after-function-or-class", + "blank-lines-before-nested-definition", + "blank-lines-top-level", "explicit-string-concatenation", "indent-with-spaces", "indentation-with-invalid-multiple", @@ -68,6 +73,7 @@ "surrounding-whitespace", "tab-indentation", "too-few-spaces-before-inline-comment", + "too-many-blank-lines", "too-many-boolean-expressions", "trailing-comma-on-bare-tuple", "triple-single-quotes", From 624c7a59cbbc5ec05cf6c06f8205c779b8ffe942 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 10 Nov 2023 21:30:28 +0900 Subject: [PATCH 002/122] Keep track of previous streak of blank lines to fix 301. --- .../ruff_linter/src/checkers/logical_lines.rs | 6 +- crates/ruff_linter/src/codes.rs | 6 ++ crates/ruff_linter/src/registry.rs | 1 - .../rules/logical_lines/blank_lines.rs | 70 +++++++++---------- .../pycodestyle/rules/logical_lines/mod.rs | 7 +- 5 files changed, 50 insertions(+), 40 deletions(-) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 07c77fae1c3b9..0dd48f7edad34 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -40,6 +40,7 @@ pub(crate) fn check_logical_lines( let mut context = LogicalLinesContext::new(settings); let mut blank_lines_tracking_vars = BlankLinesTrackingVars::default(); + let mut non_comment_prev_line = None; let mut prev_line = None; let mut prev_indent_level = None; let indent_char = stylist.indentation().as_char(); @@ -92,7 +93,7 @@ pub(crate) fn check_logical_lines( for kind in indentation( &line, - prev_line.as_ref(), + non_comment_prev_line.as_ref(), indent_char, indent_level, prev_indent_level, @@ -114,9 +115,10 @@ pub(crate) fn check_logical_lines( ); if !line.is_comment_only() { - prev_line = Some(line); + non_comment_prev_line = Some(line.clone()); prev_indent_level = Some(indent_level); } + prev_line = Some(line); } context.diagnostics } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index cfd9b938b65c2..8af59230b0580 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -132,11 +132,17 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pycodestyle, "E274") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TabBeforeKeyword), #[allow(deprecated)] (Pycodestyle, "E275") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAfterKeyword), + #[allow(deprecated)] (Pycodestyle, "E301") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLineBetweenMethods), + #[allow(deprecated)] (Pycodestyle, "E302") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLinesTopLevel), + #[allow(deprecated)] (Pycodestyle, "E303") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TooManyBlankLines), + #[allow(deprecated)] (Pycodestyle, "E304") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLineAfterDecorator), + #[allow(deprecated)] (Pycodestyle, "E305") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLinesAfterFunctionOrClass), + #[allow(deprecated)] (Pycodestyle, "E306") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLinesBeforeNestedDefinition), (Pycodestyle, "E401") => (RuleGroup::Stable, rules::pycodestyle::rules::MultipleImportsOnOneLine), (Pycodestyle, "E402") => (RuleGroup::Stable, rules::pycodestyle::rules::ModuleImportNotAtTopOfFile), diff --git a/crates/ruff_linter/src/registry.rs b/crates/ruff_linter/src/registry.rs index c92e36fbfc2a0..c9423c49da488 100644 --- a/crates/ruff_linter/src/registry.rs +++ b/crates/ruff_linter/src/registry.rs @@ -305,7 +305,6 @@ impl Rule { | Rule::BlankLinesBeforeNestedDefinition | Rule::BlankLinesTopLevel | Rule::IndentationWithInvalidMultiple - | Rule::IndentationWithInvalidMultiple | Rule::IndentationWithInvalidMultipleComment | Rule::MissingWhitespace | Rule::MissingWhitespaceAfterKeyword diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 0bfc0a162bc38..6ca5354af3236 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -14,7 +14,7 @@ use crate::checkers::logical_lines::LogicalLinesContext; use super::LogicalLine; /// Contains variables used for the linting of blank lines. -#[derive(Default)] +#[derive(Default, Debug)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct BlankLinesTrackingVars { follows_decorator: bool, @@ -329,22 +329,28 @@ pub(crate) fn blank_lines( tracked_vars.is_in_fn = false; } - for (token_idx, token) in line.tokens().iter().enumerate() { + if let Some(token) = line.tokens_trimmed().first() { if token.kind() == TokenKind::Def + // Only applies to method. && tracked_vars.is_in_class - && line.line.preceding_blank_lines == 0 - && !tracked_vars.follows_decorator - && prev_line - .and_then(|prev_line| prev_line.tokens_trimmed().first()) - .map_or(false, |token| { - !matches!(token.kind(), TokenKind::Def | TokenKind::Class) - }) + && ( + // A comment before the def is allowed (as long as it is preceded by a blank line). + (line.line.preceding_blank_lines == 0 && line.line.blank_lines == 0 && prev_line.is_some_and(|line| line.is_comment_only())) + // Standard case. + || line.line.blank_lines == 0 + && prev_line + .and_then(|prev_line| prev_line.tokens_trimmed().first()) + .map_or(false, |token| { + !matches!( + token.kind(), + TokenKind::Def | TokenKind::Class | TokenKind::At + ) + }) + ) { // E301 - let mut diagnostic = Diagnostic::new( - BlankLineBetweenMethods(line.line.preceding_blank_lines), - token.range, - ); + let mut diagnostic = + Diagnostic::new(BlankLineBetweenMethods(line.line.blank_lines), token.range); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().as_str().to_string(), locator.line_start(token.range.start()), @@ -362,33 +368,28 @@ pub(crate) fn blank_lines( && prev_line .and_then(|prev_line| prev_line.tokens_trimmed().first()) .map_or(false, |token| !matches!(token.kind(), TokenKind::Except)) - && line.line.preceding_blank_lines < 2 + && line.line.blank_lines < 2 && prev_line.is_some() { // E302 - let mut diagnostic = Diagnostic::new( - BlankLinesTopLevel(line.line.preceding_blank_lines), - token.range, - ); + let mut diagnostic = + Diagnostic::new(BlankLinesTopLevel(line.line.blank_lines), token.range); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist .line_ending() .as_str() .to_string() - .repeat(2 - line.line.preceding_blank_lines as usize), + .repeat(2 - line.line.blank_lines as usize), locator.line_start(token.range.start()), ))); context.push_diagnostic(diagnostic); - } else if token_idx == 0 - && (line.line.preceding_blank_lines > BlankLinesConfig::TOP_LEVEL - || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) - && line.line.preceding_blank_lines > BlankLinesConfig::METHOD)) + } else if line.line.blank_lines > BlankLinesConfig::TOP_LEVEL + || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) + && line.line.blank_lines > BlankLinesConfig::METHOD) { // E303 - let mut diagnostic = Diagnostic::new( - TooManyBlankLines(line.line.preceding_blank_lines), - token.range, - ); + let mut diagnostic = + Diagnostic::new(TooManyBlankLines(line.line.blank_lines), token.range); let chars_to_remove = if indent_level > 0 { line.line.preceding_blank_characters - BlankLinesConfig::METHOD @@ -400,7 +401,7 @@ pub(crate) fn blank_lines( diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); context.push_diagnostic(diagnostic); - } else if tracked_vars.follows_decorator && line.line.preceding_blank_lines > 0 { + } else if tracked_vars.follows_decorator && line.line.blank_lines > 0 { // E304 let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, token.range); @@ -411,13 +412,13 @@ pub(crate) fn blank_lines( locator.line_start(range.start()), ))); context.push_diagnostic(diagnostic); - } else if line.line.preceding_blank_lines < 2 + } else if line.line.blank_lines < 2 && (tracked_vars.is_in_fn || tracked_vars.is_in_class) && indent_level == 0 { // E305 let mut diagnostic = Diagnostic::new( - BlankLinesAfterFunctionOrClass(line.line.preceding_blank_lines), + BlankLinesAfterFunctionOrClass(line.line.blank_lines), token.range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( @@ -425,17 +426,17 @@ pub(crate) fn blank_lines( .line_ending() .as_str() .to_string() - .repeat(2 - line.line.preceding_blank_lines as usize), + .repeat(2 - line.line.blank_lines as usize), locator.line_start(token.range.start()), ))); context.push_diagnostic(diagnostic); } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class) && (tracked_vars.is_in_class || tracked_vars.is_in_fn) - && line.line.preceding_blank_lines == 0 + && line.line.blank_lines == 0 { // E306 let mut diagnostic = Diagnostic::new( - BlankLinesBeforeNestedDefinition(line.line.preceding_blank_lines), + BlankLinesBeforeNestedDefinition(line.line.blank_lines), token.range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( @@ -454,12 +455,10 @@ pub(crate) fn blank_lines( tracked_vars.is_in_class = true; tracked_vars.follows_decorator = false; tracked_vars.follows_def = false; - break; } TokenKind::At => { tracked_vars.follows_decorator = true; tracked_vars.follows_def = false; - break; } TokenKind::Def => { if !tracked_vars.is_in_fn { @@ -468,7 +467,6 @@ pub(crate) fn blank_lines( tracked_vars.is_in_fn = true; tracked_vars.follows_def = true; tracked_vars.follows_decorator = false; - break; } _ => { tracked_vars.follows_decorator = false; diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs index 80213cf340aac..421f3e6b0665c 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs @@ -487,7 +487,11 @@ impl LogicalLinesBuilder { } else { self.lines.push(Line { flags: self.current_line.flags, - preceding_blank_lines: self.current_blank_lines, + blank_lines: self.current_blank_lines, + preceding_blank_lines: self + .lines + .last() + .map_or(0, |previous_line| previous_line.blank_lines), preceding_blank_characters: self.current_blank_characters, tokens_start: self.current_line.tokens_start, tokens_end: end, @@ -517,6 +521,7 @@ impl LogicalLinesBuilder { #[derive(Debug, Clone)] struct Line { flags: TokenFlags, + blank_lines: u32, preceding_blank_lines: u32, preceding_blank_characters: u32, tokens_start: u32, From cd02e117860dd47c9bfa69fd566a137b78080fd2 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 18:25:56 +0900 Subject: [PATCH 003/122] Fix 302. --- .../rules/logical_lines/blank_lines.rs | 82 +++++++++++++++---- .../pycodestyle/rules/logical_lines/mod.rs | 13 ++- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 6ca5354af3236..a197c96395a36 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -14,7 +14,7 @@ use crate::checkers::logical_lines::LogicalLinesContext; use super::LogicalLine; /// Contains variables used for the linting of blank lines. -#[derive(Default, Debug)] +#[derive(Debug)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct BlankLinesTrackingVars { follows_decorator: bool, @@ -25,6 +25,22 @@ pub(crate) struct BlankLinesTrackingVars { is_in_fn: bool, /// The indent level where the function started. fn_indent_level: usize, + /// First line that is not a comment. + is_first_logical_line: bool, +} + +impl Default for BlankLinesTrackingVars { + fn default() -> BlankLinesTrackingVars { + BlankLinesTrackingVars { + follows_decorator: false, + follows_def: false, + is_in_class: false, + class_indent_level: 0, + is_in_fn: false, + fn_indent_level: 0, + is_first_logical_line: true, + } + } } /// Number of blank lines between various code parts. @@ -311,6 +327,12 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { } } +/// Check if the given line starts with a decorator. +fn is_decorator(line: Option<&LogicalLine>) -> bool { + line.and_then(|line| line.tokens_trimmed().first()) + .map_or(false, |token| matches!(token.kind(), TokenKind::At)) +} + /// E301, E302, E303, E304, E305, E306 pub(crate) fn blank_lines( line: &LogicalLine, @@ -321,6 +343,23 @@ pub(crate) fn blank_lines( stylist: &Stylist, context: &mut LogicalLinesContext, ) { + // dbg!( + // line.text(), + // &line.line.blank_lines, + // &line.line.preceding_blank_lines, + // &tracked_vars + // ); + // dbg!(is_decorator(prev_line)); + // dbg!(); + + if tracked_vars.is_first_logical_line { + if !line.is_comment_only() { + tracked_vars.is_first_logical_line = false; + } + // Don't expect blank lines before the first line non comment line. + return; + } + if indent_level <= tracked_vars.class_indent_level { tracked_vars.is_in_class = false; } @@ -329,7 +368,7 @@ pub(crate) fn blank_lines( tracked_vars.is_in_fn = false; } - if let Some(token) = line.tokens_trimmed().first() { + for (token_idx, token) in line.tokens().iter().enumerate() { if token.kind() == TokenKind::Def // Only applies to method. && tracked_vars.is_in_class @@ -356,18 +395,27 @@ pub(crate) fn blank_lines( locator.line_start(token.range.start()), ))); context.push_diagnostic(diagnostic); - } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class) - && !(tracked_vars.follows_decorator - || tracked_vars.is_in_class - || tracked_vars.is_in_fn - || tracked_vars.follows_def + } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class | TokenKind::At) + && !( + // Allow decorators. + is_decorator(prev_line) + // Allow groups of one-liners. + || (tracked_vars.follows_def && line - .tokens_trimmed() - .last() - .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon))) + .tokens_trimmed() + .last() + .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + ) + // Only apply to top level functions. + || tracked_vars.is_in_class + || tracked_vars.is_in_fn + // If a comment is preceding the def/class, the 2 blank lines can be before that comment. + || (line.line.preceding_blank_lines >= 2 && prev_line.map_or(false, |prev_line| prev_line.is_comment_only())) + ) + // Allow directly following an except. && prev_line - .and_then(|prev_line| prev_line.tokens_trimmed().first()) - .map_or(false, |token| !matches!(token.kind(), TokenKind::Except)) + .and_then(|prev_line| prev_line.tokens_trimmed().first()) + .map_or(true, |token| !matches!(token.kind(), TokenKind::Except)) && line.line.blank_lines < 2 && prev_line.is_some() { @@ -383,9 +431,10 @@ pub(crate) fn blank_lines( locator.line_start(token.range.start()), ))); context.push_diagnostic(diagnostic); - } else if line.line.blank_lines > BlankLinesConfig::TOP_LEVEL - || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) - && line.line.blank_lines > BlankLinesConfig::METHOD) + } else if token_idx == 0 + && (line.line.blank_lines > BlankLinesConfig::TOP_LEVEL + || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) + && line.line.blank_lines > BlankLinesConfig::METHOD)) { // E303 let mut diagnostic = @@ -455,10 +504,12 @@ pub(crate) fn blank_lines( tracked_vars.is_in_class = true; tracked_vars.follows_decorator = false; tracked_vars.follows_def = false; + break; } TokenKind::At => { tracked_vars.follows_decorator = true; tracked_vars.follows_def = false; + break; } TokenKind::Def => { if !tracked_vars.is_in_fn { @@ -467,6 +518,7 @@ pub(crate) fn blank_lines( tracked_vars.is_in_fn = true; tracked_vars.follows_def = true; tracked_vars.follows_decorator = false; + break; } _ => { tracked_vars.follows_decorator = false; diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs index 421f3e6b0665c..ec58fa33127ab 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs @@ -414,6 +414,8 @@ struct LogicalLinesBuilder { tokens: Vec, lines: Vec, current_line: CurrentLine, + /// Number of previous consecutive blank lines. + previous_blank_lines: u32, /// Number of consecutive blank lines. current_blank_lines: u32, /// Number of blank characters in the blank lines (\n vs \r\n for example). @@ -485,17 +487,20 @@ impl LogicalLinesBuilder { self.current_blank_lines += 1; self.current_blank_characters += end - self.current_line.tokens_start; } else { + if self.previous_blank_lines < self.current_blank_lines { + self.previous_blank_lines = self.current_blank_lines + } self.lines.push(Line { flags: self.current_line.flags, blank_lines: self.current_blank_lines, - preceding_blank_lines: self - .lines - .last() - .map_or(0, |previous_line| previous_line.blank_lines), + preceding_blank_lines: self.previous_blank_lines, preceding_blank_characters: self.current_blank_characters, tokens_start: self.current_line.tokens_start, tokens_end: end, }); + if self.current_line.flags != TokenFlags::COMMENT { + self.previous_blank_lines = 0; + } self.current_blank_lines = 0; self.current_blank_characters = 0; } From f9a19ef1b1e2511c9c882d7de79f3cad692db53c Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 19:26:13 +0900 Subject: [PATCH 004/122] Fix 305. --- .../rules/logical_lines/blank_lines.rs | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index a197c96395a36..a142641d4b008 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -347,9 +347,9 @@ pub(crate) fn blank_lines( // line.text(), // &line.line.blank_lines, // &line.line.preceding_blank_lines, + // indent_level, // &tracked_vars // ); - // dbg!(is_decorator(prev_line)); // dbg!(); if tracked_vars.is_first_logical_line { @@ -360,15 +360,24 @@ pub(crate) fn blank_lines( return; } - if indent_level <= tracked_vars.class_indent_level { + let mut follows_class_or_fn = false; + if indent_level <= tracked_vars.class_indent_level + && tracked_vars.is_in_class + && !line.is_comment_only() + { tracked_vars.is_in_class = false; + follows_class_or_fn = true; } - if indent_level <= tracked_vars.fn_indent_level { + if indent_level <= tracked_vars.fn_indent_level + && tracked_vars.is_in_fn + && !line.is_comment_only() + { tracked_vars.is_in_fn = false; + follows_class_or_fn = true; } - for (token_idx, token) in line.tokens().iter().enumerate() { + for (token_idx, token) in line.tokens_trimmed().iter().enumerate() { if token.kind() == TokenKind::Def // Only applies to method. && tracked_vars.is_in_class @@ -462,8 +471,16 @@ pub(crate) fn blank_lines( ))); context.push_diagnostic(diagnostic); } else if line.line.blank_lines < 2 - && (tracked_vars.is_in_fn || tracked_vars.is_in_class) + // If a comment is preceding the line, the 2 blank lines can be before that comment. + && !(line.line.preceding_blank_lines >= 2 + && prev_line.map_or(false, |prev_line| prev_line.is_comment_only())) + && follows_class_or_fn && indent_level == 0 + && !line.is_comment_only() + && !matches!( + token.kind(), + TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async + ) { // E305 let mut diagnostic = Diagnostic::new( @@ -520,9 +537,14 @@ pub(crate) fn blank_lines( tracked_vars.follows_decorator = false; break; } + TokenKind::Async => { + tracked_vars.follows_decorator = false; + tracked_vars.follows_def = false; + } _ => { tracked_vars.follows_decorator = false; tracked_vars.follows_def = false; + break; } } } From a10bea325e1ca27a38f7e9715da6756e2dc0b8ce Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 20:09:26 +0900 Subject: [PATCH 005/122] Fix 306. --- crates/ruff_linter/src/checkers/logical_lines.rs | 1 + .../pycodestyle/rules/logical_lines/blank_lines.rs | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 0dd48f7edad34..0bd95db53cfa2 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -108,6 +108,7 @@ pub(crate) fn check_logical_lines( &line, prev_line.as_ref(), &mut blank_lines_tracking_vars, + prev_indent_level, indent_level, locator, stylist, diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index a142641d4b008..f50e7214d232a 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -338,6 +338,7 @@ pub(crate) fn blank_lines( line: &LogicalLine, prev_line: Option<&LogicalLine>, tracked_vars: &mut BlankLinesTrackingVars, + prev_indent_level: Option, indent_level: usize, locator: &Locator, stylist: &Stylist, @@ -498,7 +499,16 @@ pub(crate) fn blank_lines( context.push_diagnostic(diagnostic); } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class) && (tracked_vars.is_in_class || tracked_vars.is_in_fn) - && line.line.blank_lines == 0 + && line.line.preceding_blank_lines == 0 + && !is_decorator(prev_line) + && !prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level < indent_level) + // Allow groups of one-liners. + && !(tracked_vars.follows_def + && line + .tokens_trimmed() + .last() + .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + ) { // E306 let mut diagnostic = Diagnostic::new( From f57b614257d2052add317e82bb186ed91f7bd546 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 22:40:58 +0900 Subject: [PATCH 006/122] Fix 303 regression. --- .../test/fixtures/pycodestyle/E30.py | 1 - .../rules/logical_lines/blank_lines.rs | 47 ++++++++++++------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 6ef70a60b35a3..d7e5f4c315b30 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -564,4 +564,3 @@ def f(): def f(): pass # end - diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index f50e7214d232a..5d9259c22fc3d 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -27,6 +27,9 @@ pub(crate) struct BlankLinesTrackingVars { fn_indent_level: usize, /// First line that is not a comment. is_first_logical_line: bool, + /// This needs to be tracked between lines since the `is_in_class` and `is_in_fn` are set to + /// false when a comment is set dedented, but E305 should trigger on the next non-comment line. + follows_comment_after_class_or_fn: bool, } impl Default for BlankLinesTrackingVars { @@ -39,6 +42,7 @@ impl Default for BlankLinesTrackingVars { is_in_fn: false, fn_indent_level: 0, is_first_logical_line: true, + follows_comment_after_class_or_fn: false, } } } @@ -362,23 +366,34 @@ pub(crate) fn blank_lines( } let mut follows_class_or_fn = false; - if indent_level <= tracked_vars.class_indent_level - && tracked_vars.is_in_class - && !line.is_comment_only() - { + if indent_level <= tracked_vars.class_indent_level && tracked_vars.is_in_class { tracked_vars.is_in_class = false; - follows_class_or_fn = true; + if line.is_comment_only() { + tracked_vars.follows_comment_after_class_or_fn = true + } else { + follows_class_or_fn = true; + } } - if indent_level <= tracked_vars.fn_indent_level - && tracked_vars.is_in_fn - && !line.is_comment_only() - { + if indent_level <= tracked_vars.fn_indent_level && tracked_vars.is_in_fn { tracked_vars.is_in_fn = false; + if line.is_comment_only() { + tracked_vars.follows_comment_after_class_or_fn = true + } else { + follows_class_or_fn = true; + } + } + + if tracked_vars.follows_comment_after_class_or_fn && !line.is_comment_only() { follows_class_or_fn = true; + tracked_vars.follows_comment_after_class_or_fn = false; } - for (token_idx, token) in line.tokens_trimmed().iter().enumerate() { + for token in line.tokens().iter() { + if matches!(token.kind, TokenKind::Indent | TokenKind::Dedent) { + continue; + } + if token.kind() == TokenKind::Def // Only applies to method. && tracked_vars.is_in_class @@ -441,10 +456,9 @@ pub(crate) fn blank_lines( locator.line_start(token.range.start()), ))); context.push_diagnostic(diagnostic); - } else if token_idx == 0 - && (line.line.blank_lines > BlankLinesConfig::TOP_LEVEL - || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) - && line.line.blank_lines > BlankLinesConfig::METHOD)) + } else if line.line.blank_lines > BlankLinesConfig::TOP_LEVEL + || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) + && line.line.blank_lines > BlankLinesConfig::METHOD) { // E303 let mut diagnostic = @@ -471,10 +485,7 @@ pub(crate) fn blank_lines( locator.line_start(range.start()), ))); context.push_diagnostic(diagnostic); - } else if line.line.blank_lines < 2 - // If a comment is preceding the line, the 2 blank lines can be before that comment. - && !(line.line.preceding_blank_lines >= 2 - && prev_line.map_or(false, |prev_line| prev_line.is_comment_only())) + } else if line.line.preceding_blank_lines < BlankLinesConfig::TOP_LEVEL && follows_class_or_fn && indent_level == 0 && !line.is_comment_only() From 42c6f12f993465e5742bd3023abe2875beafe285 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 22:48:01 +0900 Subject: [PATCH 007/122] Add snapshots. --- .../ruff_linter/src/rules/pycodestyle/mod.rs | 32 +-- ...ules__pycodestyle__tests__E301_E30.py.snap | 44 +++++ ...ules__pycodestyle__tests__E302_E30.py.snap | 187 ++++++++++++++++++ ...ules__pycodestyle__tests__E303_E30.py.snap | 128 ++++++++++++ ...ules__pycodestyle__tests__E304_E30.py.snap | 24 +++ ...ules__pycodestyle__tests__E305_E30.py.snap | 102 ++++++++++ ...ules__pycodestyle__tests__E306_E30.py.snap | 163 +++++++++++++++ ruff.schema.json | 4 +- 8 files changed, 658 insertions(+), 26 deletions(-) create mode 100644 crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap create mode 100644 crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap create mode 100644 crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap create mode 100644 crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap create mode 100644 crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap create mode 100644 crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap diff --git a/crates/ruff_linter/src/rules/pycodestyle/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/mod.rs index ace20dd42761b..dbf21c8a12629 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/mod.rs @@ -122,31 +122,6 @@ mod tests { #[test_case(Rule::TooFewSpacesBeforeInlineComment, Path::new("E26.py"))] #[test_case(Rule::UnexpectedIndentation, Path::new("E11.py"))] #[test_case(Rule::UnexpectedIndentationComment, Path::new("E11.py"))] - #[test_case(Rule::UnderIndentedHangingIndent, Path::new("E12.py"))] - #[test_case(Rule::MissingOrOutdentedIndentation, Path::new("E12.py"))] - #[test_case( - Rule::ClosingBracketNotMatchingOpeningBracketIndentation, - Path::new("E12.py") - )] - #[test_case( - Rule::ClosingBracketNotMatchingOpeningBracketVisualIndentation, - Path::new("E12.py") - )] - #[test_case(Rule::ContinuationLineIndentSameAsNextLogicalLine, Path::new("E12.py"))] - #[test_case( - Rule::ContinuationLineOverIndentedForHangingIndent, - Path::new("E12.py") - )] - #[test_case(Rule::ContinuationLineOverIndentedForVisualIndent, Path::new("E12.py"))] - #[test_case( - Rule::ContinuationLineUnderIndentedForVisualIndent, - Path::new("E12.py") - )] - #[test_case( - Rule::VisuallyIndentedLineWithSameIndentAsNextLogicalLine, - Path::new("E12.py") - )] - #[test_case(Rule::ContinuationLineUnalignedForHangingIndent, Path::new("E12.py"))] #[test_case(Rule::WhitespaceAfterOpenBracket, Path::new("E20.py"))] #[test_case(Rule::WhitespaceBeforeCloseBracket, Path::new("E20.py"))] #[test_case(Rule::WhitespaceBeforePunctuation, Path::new("E20.py"))] @@ -156,6 +131,13 @@ mod tests { Path::new("E25.py") )] #[test_case(Rule::MissingWhitespaceAroundParameterEquals, Path::new("E25.py"))] + #[test_case(Rule::BlankLineBetweenMethods, Path::new("E30.py"))] + #[test_case(Rule::BlankLinesTopLevel, Path::new("E30.py"))] + #[test_case(Rule::TooManyBlankLines, Path::new("E30.py"))] + #[test_case(Rule::BlankLineAfterDecorator, Path::new("E30.py"))] + #[test_case(Rule::BlankLinesAfterFunctionOrClass, Path::new("E30.py"))] + #[test_case(Rule::BlankLinesBeforeNestedDefinition, Path::new("E30.py"))] + fn logical(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap new file mode 100644 index 0000000000000..bf4b0f2ab1666 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -0,0 +1,44 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +E30.py:273:5: E301 [*] Expected 1 blank line, found 0 + | +271 | def func1(): +272 | pass +273 | def func2(): + | ^^^ E301 +274 | pass +275 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +270 270 | +271 271 | def func1(): +272 272 | pass + 273 |+ +273 274 | def func2(): +274 275 | pass +275 276 | # end + +E30.py:284:5: E301 [*] Expected 1 blank line, found 0 + | +282 | pass +283 | # comment +284 | def fn2(): + | ^^^ E301 +285 | pass +286 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +281 281 | def fn1(): +282 282 | pass +283 283 | # comment + 284 |+ +284 285 | def fn2(): +285 286 | pass +286 287 | # end + + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap new file mode 100644 index 0000000000000..511ab6ec57f9c --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -0,0 +1,187 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +E30.py:291:1: E302 [*] Expected 2 blank lines, found 0 + | +289 | # E302 +290 | """Main module.""" +291 | def fn(): + | ^^^ E302 +292 | pass +293 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +288 288 | +289 289 | # E302 +290 290 | """Main module.""" + 291 |+ + 292 |+ +291 293 | def fn(): +292 294 | pass +293 295 | # end + +E30.py:298:1: E302 [*] Expected 2 blank lines, found 0 + | +296 | # E302 +297 | import sys +298 | def get_sys_path(): + | ^^^ E302 +299 | return sys.path +300 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +295 295 | +296 296 | # E302 +297 297 | import sys + 298 |+ + 299 |+ +298 300 | def get_sys_path(): +299 301 | return sys.path +300 302 | # end + +E30.py:307:1: E302 [*] Expected 2 blank lines, found 1 + | +305 | pass +306 | +307 | def b(): + | ^^^ E302 +308 | pass +309 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +304 304 | def a(): +305 305 | pass +306 306 | + 307 |+ +307 308 | def b(): +308 309 | pass +309 310 | # end + +E30.py:318:1: E302 [*] Expected 2 blank lines, found 1 + | +316 | # comment +317 | +318 | def b(): + | ^^^ E302 +319 | pass +320 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +315 315 | +316 316 | # comment +317 317 | + 318 |+ +318 319 | def b(): +319 320 | pass +320 321 | # end + +E30.py:327:7: E302 [*] Expected 2 blank lines, found 1 + | +325 | pass +326 | +327 | async def b(): + | ^^^ E302 +328 | pass +329 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +324 324 | def a(): +325 325 | pass +326 326 | + 327 |+ +327 328 | async def b(): +328 329 | pass +329 330 | # end + +E30.py:336:8: E302 [*] Expected 2 blank lines, found 1 + | +334 | pass +335 | +336 | async def x(y: int = 1): + | ^^^ E302 +337 | pass +338 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +333 333 | async def x(): +334 334 | pass +335 335 | + 336 |+ +336 337 | async def x(y: int = 1): +337 338 | pass +338 339 | # end + +E30.py:344:1: E302 [*] Expected 2 blank lines, found 0 + | +342 | def bar(): +343 | pass +344 | def baz(): pass + | ^^^ E302 +345 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +341 341 | # E302 +342 342 | def bar(): +343 343 | pass + 344 |+ + 345 |+ +344 346 | def baz(): pass +345 347 | # end +346 348 | + +E30.py:350:1: E302 [*] Expected 2 blank lines, found 0 + | +348 | # E302 +349 | def bar(): pass +350 | def baz(): + | ^^^ E302 +351 | pass +352 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +347 347 | +348 348 | # E302 +349 349 | def bar(): pass + 350 |+ + 351 |+ +350 352 | def baz(): +351 353 | pass +352 354 | # end + +E30.py:360:1: E302 [*] Expected 2 blank lines, found 0 + | +359 | # comment +360 | @decorator + | ^ E302 +361 | def g(): +362 | pass + | + = help: Add missing blank line(s) + +ℹ Fix +357 357 | pass +358 358 | +359 359 | # comment + 360 |+ + 361 |+ +360 362 | @decorator +361 363 | def g(): +362 364 | pass + + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap new file mode 100644 index 0000000000000..91cfae38268ca --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -0,0 +1,128 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +E30.py:371:5: E303 [*] Too many blank lines (2) + | +371 | # arbitrary comment + | ^^^^^^^^^^^^^^^^^^^ E303 +372 | +373 | def inner(): # E306 not expected + | + = help: Remove extraneous blank line(s) + +ℹ Fix +367 367 | def fn(): +368 368 | _ = None +369 369 | +370 |- +371 370 | # arbitrary comment +372 371 | +373 372 | def inner(): # E306 not expected + +E30.py:383:5: E303 [*] Too many blank lines (2) + | +383 | # arbitrary comment + | ^^^^^^^^^^^^^^^^^^^ E303 +384 | def inner(): # E306 not expected +385 | pass + | + = help: Remove extraneous blank line(s) + +ℹ Fix +379 379 | def fn(): +380 380 | _ = None +381 381 | +382 |- +383 382 | # arbitrary comment +384 383 | def inner(): # E306 not expected +385 384 | pass + +E30.py:394:1: E303 [*] Too many blank lines (3) + | +394 | print() + | ^^^^^ E303 +395 | # end + | + = help: Remove extraneous blank line(s) + +ℹ Fix +390 390 | print() +391 391 | +392 392 | +393 |- +394 393 | print() +395 394 | # end +396 395 | + +E30.py:403:1: E303 [*] Too many blank lines (3) + | +403 | # comment + | ^^^^^^^^^ E303 +404 | +405 | print() + | + = help: Remove extraneous blank line(s) + +ℹ Fix +399 399 | print() +400 400 | +401 401 | +402 |- +403 402 | # comment +404 403 | +405 404 | print() + +E30.py:414:5: E303 [*] Too many blank lines (2) + | +414 | # comment + | ^^^^^^^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Fix +410 410 | def a(): +411 411 | print() +412 412 | +413 |- +414 413 | # comment +415 414 | +416 415 | + +E30.py:417:5: E303 [*] Too many blank lines (2) + | +417 | # another comment + | ^^^^^^^^^^^^^^^^^ E303 +418 | +419 | print() + | + = help: Remove extraneous blank line(s) + +ℹ Fix +413 413 | +414 414 | # comment +415 415 | +416 |- +417 416 | # another comment +418 417 | +419 418 | print() + +E30.py:428:1: E303 [*] Too many blank lines (3) + | +428 | / """This class docstring comes on line 5. +429 | | It gives error E303: too many blank lines (3) +430 | | """ + | |___^ E303 +431 | # end + | + = help: Remove extraneous blank line(s) + +ℹ Fix +424 424 | #!python +425 425 | +426 426 | +427 |- +428 427 | """This class docstring comes on line 5. +429 428 | It gives error E303: too many blank lines (3) +430 429 | """ + + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap new file mode 100644 index 0000000000000..6744b896ca7e6 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -0,0 +1,24 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +E30.py:437:1: E304 [*] blank lines found after function decorator + | +435 | @decorator +436 | +437 | def function(): + | ^^^ E304 +438 | pass +439 | # end + | + = help: Remove extraneous blank line(s) + +ℹ Fix +433 433 | +434 434 | # E304 +435 435 | @decorator +436 |- +437 436 | def function(): +438 437 | pass +439 438 | # end + + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap new file mode 100644 index 0000000000000..0b3c2e6cf0cdf --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -0,0 +1,102 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +E30.py:449:1: E305 [*] expected 2 blank lines after class or function definition, found (0) + | +448 | # another comment +449 | fn() + | ^^ E305 +450 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +446 446 | # comment +447 447 | +448 448 | # another comment + 449 |+ + 450 |+ +449 451 | fn() +450 452 | # end +451 453 | + +E30.py:460:1: E305 [*] expected 2 blank lines after class or function definition, found (0) + | +459 | # another comment +460 | a = 1 + | ^ E305 +461 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +457 457 | # comment +458 458 | +459 459 | # another comment + 460 |+ + 461 |+ +460 462 | a = 1 +461 463 | # end +462 464 | + +E30.py:472:1: E305 [*] expected 2 blank lines after class or function definition, found (1) + | +470 | # another comment +471 | +472 | try: + | ^^^ E305 +473 | fn() +474 | except Exception: + | + = help: Add missing blank line(s) + +ℹ Fix +469 469 | +470 470 | # another comment +471 471 | + 472 |+ +472 473 | try: +473 474 | fn() +474 475 | except Exception: + +E30.py:484:1: E305 [*] expected 2 blank lines after class or function definition, found (0) + | +483 | # Two spaces before comments, too. +484 | if a(): + | ^^ E305 +485 | a() +486 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +481 481 | print +482 482 | +483 483 | # Two spaces before comments, too. + 484 |+ + 485 |+ +484 486 | if a(): +485 487 | a() +486 488 | # end + +E30.py:497:1: E305 [*] expected 2 blank lines after class or function definition, found (1) + | +495 | blah, blah +496 | +497 | if __name__ == '__main__': + | ^^ E305 +498 | main() +499 | # end + | + = help: Add missing blank line(s) + +ℹ Fix +494 494 | def main(): +495 495 | blah, blah +496 496 | + 497 |+ +497 498 | if __name__ == '__main__': +498 499 | main() +499 500 | # end + + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap new file mode 100644 index 0000000000000..3626275548dc4 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -0,0 +1,163 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +E30.py:505:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +503 | def a(): +504 | x = 1 +505 | def b(): + | ^^^ E306 +506 | pass +507 | # end + | + = help: Add missing blank line + +ℹ Fix +502 502 | # E306:3:5 +503 503 | def a(): +504 504 | x = 1 + 505 |+ +505 506 | def b(): +506 507 | pass +507 508 | # end + +E30.py:513:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +511 | async def a(): +512 | x = 1 +513 | def b(): + | ^^^ E306 +514 | pass +515 | # end + | + = help: Add missing blank line + +ℹ Fix +510 510 | #: E306:3:5 +511 511 | async def a(): +512 512 | x = 1 + 513 |+ +513 514 | def b(): +514 515 | pass +515 516 | # end + +E30.py:521:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +519 | def a(): +520 | x = 2 +521 | def b(): + | ^^^ E306 +522 | x = 1 +523 | def c(): + | + = help: Add missing blank line + +ℹ Fix +518 518 | #: E306:3:5 E306:5:9 +519 519 | def a(): +520 520 | x = 2 + 521 |+ +521 522 | def b(): +522 523 | x = 1 +523 524 | def c(): + +E30.py:523:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +521 | def b(): +522 | x = 1 +523 | def c(): + | ^^^ E306 +524 | pass +525 | # end + | + = help: Add missing blank line + +ℹ Fix +520 520 | x = 2 +521 521 | def b(): +522 522 | x = 1 + 523 |+ +523 524 | def c(): +524 525 | pass +525 526 | # end + +E30.py:531:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +529 | def a(): +530 | x = 1 +531 | class C: + | ^^^^^ E306 +532 | pass +533 | x = 2 + | + = help: Add missing blank line + +ℹ Fix +528 528 | # E306:3:5 E306:6:5 +529 529 | def a(): +530 530 | x = 1 + 531 |+ +531 532 | class C: +532 533 | pass +533 534 | x = 2 + +E30.py:534:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +532 | pass +533 | x = 2 +534 | def b(): + | ^^^ E306 +535 | pass +536 | # end + | + = help: Add missing blank line + +ℹ Fix +531 531 | class C: +532 532 | pass +533 533 | x = 2 + 534 |+ +534 535 | def b(): +535 536 | pass +536 537 | # end + +E30.py:543:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +541 | def bar(): +542 | pass +543 | def baz(): pass + | ^^^ E306 +544 | # end + | + = help: Add missing blank line + +ℹ Fix +540 540 | def foo(): +541 541 | def bar(): +542 542 | pass + 543 |+ +543 544 | def baz(): pass +544 545 | # end +545 546 | + +E30.py:550:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +548 | def foo(): +549 | def bar(): pass +550 | def baz(): + | ^^^ E306 +551 | pass +552 | # end + | + = help: Add missing blank line + +ℹ Fix +547 547 | # E306:3:5 +548 548 | def foo(): +549 549 | def bar(): pass + 550 |+ +550 551 | def baz(): +551 552 | pass +552 553 | # end + + diff --git a/ruff.schema.json b/ruff.schema.json index 57f8a9d591269..794561ac04831 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -2672,6 +2672,8 @@ "E273", "E274", "E275", + "E3", + "E30", "E301", "E302", "E303", @@ -3598,4 +3600,4 @@ "type": "string" } } -} +} \ No newline at end of file From 228301934ef5a0a78a62b42d73dd888caf0d0307 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 23:12:13 +0900 Subject: [PATCH 008/122] Update nursery rules list --- crates/ruff_workspace/src/configuration.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/ruff_workspace/src/configuration.rs b/crates/ruff_workspace/src/configuration.rs index 0f7924531a388..219142f314b99 100644 --- a/crates/ruff_workspace/src/configuration.rs +++ b/crates/ruff_workspace/src/configuration.rs @@ -1119,6 +1119,12 @@ mod tests { Rule::TabAfterKeyword, Rule::TabBeforeKeyword, Rule::MissingWhitespaceAfterKeyword, + Rule::BlankLineBetweenMethods, + Rule::BlankLinesTopLevel, + Rule::TooManyBlankLines, + Rule::BlankLineAfterDecorator, + Rule::BlankLinesAfterFunctionOrClass, + Rule::BlankLinesBeforeNestedDefinition, Rule::CompareToEmptyString, Rule::NoSelfUse, Rule::EqWithoutHash, From 06618c2f27637d0a007fb1d582359c4787d55be0 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 23:14:38 +0900 Subject: [PATCH 009/122] Add a non-error test and its corresponding fix. --- .../test/fixtures/pycodestyle/E30.py | 12 ++++++ .../ruff_linter/src/checkers/logical_lines.rs | 1 + .../rules/logical_lines/blank_lines.rs | 38 +++++++++++++------ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index d7e5f4c315b30..1bad29a9bbe3d 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -60,6 +60,18 @@ def func2(): # end +# No error +class Class(object): + + def func1(): + pass + +# comment + def func2(): + pass +# end + + # No error class Class: diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 0bd95db53cfa2..953b1b4e9ebdf 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -110,6 +110,7 @@ pub(crate) fn check_logical_lines( &mut blank_lines_tracking_vars, prev_indent_level, indent_level, + indent_size, locator, stylist, &mut context, diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 5d9259c22fc3d..ad52c9a277b51 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -29,7 +29,8 @@ pub(crate) struct BlankLinesTrackingVars { is_first_logical_line: bool, /// This needs to be tracked between lines since the `is_in_class` and `is_in_fn` are set to /// false when a comment is set dedented, but E305 should trigger on the next non-comment line. - follows_comment_after_class_or_fn: bool, + follows_comment_after_fn: bool, + follows_comment_after_class: bool, } impl Default for BlankLinesTrackingVars { @@ -42,7 +43,8 @@ impl Default for BlankLinesTrackingVars { is_in_fn: false, fn_indent_level: 0, is_first_logical_line: true, - follows_comment_after_class_or_fn: false, + follows_comment_after_fn: false, + follows_comment_after_class: false, } } } @@ -344,6 +346,7 @@ pub(crate) fn blank_lines( tracked_vars: &mut BlankLinesTrackingVars, prev_indent_level: Option, indent_level: usize, + indent_size: usize, locator: &Locator, stylist: &Stylist, context: &mut LogicalLinesContext, @@ -366,27 +369,40 @@ pub(crate) fn blank_lines( } let mut follows_class_or_fn = false; - if indent_level <= tracked_vars.class_indent_level && tracked_vars.is_in_class { + if indent_level < tracked_vars.class_indent_level && tracked_vars.is_in_class { tracked_vars.is_in_class = false; if line.is_comment_only() { - tracked_vars.follows_comment_after_class_or_fn = true + tracked_vars.follows_comment_after_class = true } else { follows_class_or_fn = true; } } - if indent_level <= tracked_vars.fn_indent_level && tracked_vars.is_in_fn { + if indent_level < tracked_vars.fn_indent_level && tracked_vars.is_in_fn { tracked_vars.is_in_fn = false; if line.is_comment_only() { - tracked_vars.follows_comment_after_class_or_fn = true + tracked_vars.follows_comment_after_fn = true } else { follows_class_or_fn = true; } } - if tracked_vars.follows_comment_after_class_or_fn && !line.is_comment_only() { - follows_class_or_fn = true; - tracked_vars.follows_comment_after_class_or_fn = false; + if tracked_vars.follows_comment_after_fn && !line.is_comment_only() { + if indent_level == tracked_vars.fn_indent_level { + tracked_vars.is_in_fn = true; + } else { + follows_class_or_fn = true; + } + tracked_vars.follows_comment_after_fn = false; + } + + if tracked_vars.follows_comment_after_class && !line.is_comment_only() { + if indent_level == tracked_vars.class_indent_level { + tracked_vars.is_in_class = true; + } else { + follows_class_or_fn = true; + } + tracked_vars.follows_comment_after_class = false; } for token in line.tokens().iter() { @@ -537,7 +553,7 @@ pub(crate) fn blank_lines( match token.kind() { TokenKind::Class => { if !tracked_vars.is_in_class { - tracked_vars.class_indent_level = indent_level; + tracked_vars.class_indent_level = indent_level + indent_size; } tracked_vars.is_in_class = true; tracked_vars.follows_decorator = false; @@ -551,7 +567,7 @@ pub(crate) fn blank_lines( } TokenKind::Def => { if !tracked_vars.is_in_fn { - tracked_vars.fn_indent_level = indent_level; + tracked_vars.fn_indent_level = indent_level + indent_size; } tracked_vars.is_in_fn = true; tracked_vars.follows_def = true; From 09b6ba1ee824ad44b3a9ba7ed90592d12d201baf Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 23:31:13 +0900 Subject: [PATCH 010/122] Add a TODO comment. --- .../src/rules/pycodestyle/rules/logical_lines/blank_lines.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index ad52c9a277b51..f543816d1a527 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -431,6 +431,8 @@ pub(crate) fn blank_lines( // E301 let mut diagnostic = Diagnostic::new(BlankLineBetweenMethods(line.line.blank_lines), token.range); + // TODO: in the case where there is a comment between two methods, make the comment "stick" + // to the second method instead of the first. diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().as_str().to_string(), locator.line_start(token.range.start()), From 1bdd1815983e9237b69c22a059b906ec97dad6ad Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 23:41:04 +0900 Subject: [PATCH 011/122] Update snapshots following 06618c2. --- ...ules__pycodestyle__tests__E301_E30.py.snap | 52 ++-- ...ules__pycodestyle__tests__E302_E30.py.snap | 272 +++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 150 +++++----- ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 ++++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 234 +++++++-------- 6 files changed, 430 insertions(+), 430 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index bf4b0f2ab1666..e722fff7a4559 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:273:5: E301 [*] Expected 1 blank line, found 0 +E30.py:285:5: E301 [*] Expected 1 blank line, found 0 | -271 | def func1(): -272 | pass -273 | def func2(): +283 | def func1(): +284 | pass +285 | def func2(): | ^^^ E301 -274 | pass -275 | # end +286 | pass +287 | # end | = help: Add missing blank line(s) ℹ Fix -270 270 | -271 271 | def func1(): -272 272 | pass - 273 |+ -273 274 | def func2(): -274 275 | pass -275 276 | # end +282 282 | +283 283 | def func1(): +284 284 | pass + 285 |+ +285 286 | def func2(): +286 287 | pass +287 288 | # end -E30.py:284:5: E301 [*] Expected 1 blank line, found 0 +E30.py:296:5: E301 [*] Expected 1 blank line, found 0 | -282 | pass -283 | # comment -284 | def fn2(): +294 | pass +295 | # comment +296 | def fn2(): | ^^^ E301 -285 | pass -286 | # end +297 | pass +298 | # end | = help: Add missing blank line(s) ℹ Fix -281 281 | def fn1(): -282 282 | pass -283 283 | # comment - 284 |+ -284 285 | def fn2(): -285 286 | pass -286 287 | # end +293 293 | def fn1(): +294 294 | pass +295 295 | # comment + 296 |+ +296 297 | def fn2(): +297 298 | pass +298 299 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 511ab6ec57f9c..590e595fe5ef5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,187 +1,187 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:291:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:303:1: E302 [*] Expected 2 blank lines, found 0 | -289 | # E302 -290 | """Main module.""" -291 | def fn(): +301 | # E302 +302 | """Main module.""" +303 | def fn(): | ^^^ E302 -292 | pass -293 | # end +304 | pass +305 | # end | = help: Add missing blank line(s) ℹ Fix -288 288 | -289 289 | # E302 -290 290 | """Main module.""" - 291 |+ - 292 |+ -291 293 | def fn(): -292 294 | pass -293 295 | # end - -E30.py:298:1: E302 [*] Expected 2 blank lines, found 0 - | -296 | # E302 -297 | import sys -298 | def get_sys_path(): +300 300 | +301 301 | # E302 +302 302 | """Main module.""" + 303 |+ + 304 |+ +303 305 | def fn(): +304 306 | pass +305 307 | # end + +E30.py:310:1: E302 [*] Expected 2 blank lines, found 0 + | +308 | # E302 +309 | import sys +310 | def get_sys_path(): | ^^^ E302 -299 | return sys.path -300 | # end +311 | return sys.path +312 | # end | = help: Add missing blank line(s) ℹ Fix -295 295 | -296 296 | # E302 -297 297 | import sys - 298 |+ - 299 |+ -298 300 | def get_sys_path(): -299 301 | return sys.path -300 302 | # end - -E30.py:307:1: E302 [*] Expected 2 blank lines, found 1 - | -305 | pass -306 | -307 | def b(): +307 307 | +308 308 | # E302 +309 309 | import sys + 310 |+ + 311 |+ +310 312 | def get_sys_path(): +311 313 | return sys.path +312 314 | # end + +E30.py:319:1: E302 [*] Expected 2 blank lines, found 1 + | +317 | pass +318 | +319 | def b(): | ^^^ E302 -308 | pass -309 | # end +320 | pass +321 | # end | = help: Add missing blank line(s) ℹ Fix -304 304 | def a(): -305 305 | pass -306 306 | - 307 |+ -307 308 | def b(): -308 309 | pass -309 310 | # end - -E30.py:318:1: E302 [*] Expected 2 blank lines, found 1 - | -316 | # comment -317 | -318 | def b(): +316 316 | def a(): +317 317 | pass +318 318 | + 319 |+ +319 320 | def b(): +320 321 | pass +321 322 | # end + +E30.py:330:1: E302 [*] Expected 2 blank lines, found 1 + | +328 | # comment +329 | +330 | def b(): | ^^^ E302 -319 | pass -320 | # end +331 | pass +332 | # end | = help: Add missing blank line(s) ℹ Fix -315 315 | -316 316 | # comment -317 317 | - 318 |+ -318 319 | def b(): -319 320 | pass -320 321 | # end - -E30.py:327:7: E302 [*] Expected 2 blank lines, found 1 - | -325 | pass -326 | -327 | async def b(): +327 327 | +328 328 | # comment +329 329 | + 330 |+ +330 331 | def b(): +331 332 | pass +332 333 | # end + +E30.py:339:7: E302 [*] Expected 2 blank lines, found 1 + | +337 | pass +338 | +339 | async def b(): | ^^^ E302 -328 | pass -329 | # end +340 | pass +341 | # end | = help: Add missing blank line(s) ℹ Fix -324 324 | def a(): -325 325 | pass -326 326 | - 327 |+ -327 328 | async def b(): -328 329 | pass -329 330 | # end - -E30.py:336:8: E302 [*] Expected 2 blank lines, found 1 - | -334 | pass -335 | -336 | async def x(y: int = 1): +336 336 | def a(): +337 337 | pass +338 338 | + 339 |+ +339 340 | async def b(): +340 341 | pass +341 342 | # end + +E30.py:348:8: E302 [*] Expected 2 blank lines, found 1 + | +346 | pass +347 | +348 | async def x(y: int = 1): | ^^^ E302 -337 | pass -338 | # end +349 | pass +350 | # end | = help: Add missing blank line(s) ℹ Fix -333 333 | async def x(): -334 334 | pass -335 335 | - 336 |+ -336 337 | async def x(y: int = 1): -337 338 | pass -338 339 | # end - -E30.py:344:1: E302 [*] Expected 2 blank lines, found 0 - | -342 | def bar(): -343 | pass -344 | def baz(): pass +345 345 | async def x(): +346 346 | pass +347 347 | + 348 |+ +348 349 | async def x(y: int = 1): +349 350 | pass +350 351 | # end + +E30.py:356:1: E302 [*] Expected 2 blank lines, found 0 + | +354 | def bar(): +355 | pass +356 | def baz(): pass | ^^^ E302 -345 | # end +357 | # end | = help: Add missing blank line(s) ℹ Fix -341 341 | # E302 -342 342 | def bar(): -343 343 | pass - 344 |+ - 345 |+ -344 346 | def baz(): pass -345 347 | # end -346 348 | - -E30.py:350:1: E302 [*] Expected 2 blank lines, found 0 - | -348 | # E302 -349 | def bar(): pass -350 | def baz(): +353 353 | # E302 +354 354 | def bar(): +355 355 | pass + 356 |+ + 357 |+ +356 358 | def baz(): pass +357 359 | # end +358 360 | + +E30.py:362:1: E302 [*] Expected 2 blank lines, found 0 + | +360 | # E302 +361 | def bar(): pass +362 | def baz(): | ^^^ E302 -351 | pass -352 | # end +363 | pass +364 | # end | = help: Add missing blank line(s) ℹ Fix -347 347 | -348 348 | # E302 -349 349 | def bar(): pass - 350 |+ - 351 |+ -350 352 | def baz(): -351 353 | pass -352 354 | # end - -E30.py:360:1: E302 [*] Expected 2 blank lines, found 0 - | -359 | # comment -360 | @decorator +359 359 | +360 360 | # E302 +361 361 | def bar(): pass + 362 |+ + 363 |+ +362 364 | def baz(): +363 365 | pass +364 366 | # end + +E30.py:372:1: E302 [*] Expected 2 blank lines, found 0 + | +371 | # comment +372 | @decorator | ^ E302 -361 | def g(): -362 | pass +373 | def g(): +374 | pass | = help: Add missing blank line(s) ℹ Fix -357 357 | pass -358 358 | -359 359 | # comment - 360 |+ - 361 |+ -360 362 | @decorator -361 363 | def g(): -362 364 | pass +369 369 | pass +370 370 | +371 371 | # comment + 372 |+ + 373 |+ +372 374 | @decorator +373 375 | def g(): +374 376 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 91cfae38268ca..22b8388bbb221 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,30 +1,12 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:371:5: E303 [*] Too many blank lines (2) - | -371 | # arbitrary comment - | ^^^^^^^^^^^^^^^^^^^ E303 -372 | -373 | def inner(): # E306 not expected - | - = help: Remove extraneous blank line(s) - -ℹ Fix -367 367 | def fn(): -368 368 | _ = None -369 369 | -370 |- -371 370 | # arbitrary comment -372 371 | -373 372 | def inner(): # E306 not expected - E30.py:383:5: E303 [*] Too many blank lines (2) | 383 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -384 | def inner(): # E306 not expected -385 | pass +384 | +385 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) @@ -34,95 +16,113 @@ E30.py:383:5: E303 [*] Too many blank lines (2) 381 381 | 382 |- 383 382 | # arbitrary comment -384 383 | def inner(): # E306 not expected -385 384 | pass +384 383 | +385 384 | def inner(): # E306 not expected + +E30.py:395:5: E303 [*] Too many blank lines (2) + | +395 | # arbitrary comment + | ^^^^^^^^^^^^^^^^^^^ E303 +396 | def inner(): # E306 not expected +397 | pass + | + = help: Remove extraneous blank line(s) + +ℹ Fix +391 391 | def fn(): +392 392 | _ = None +393 393 | +394 |- +395 394 | # arbitrary comment +396 395 | def inner(): # E306 not expected +397 396 | pass -E30.py:394:1: E303 [*] Too many blank lines (3) +E30.py:406:1: E303 [*] Too many blank lines (3) | -394 | print() +406 | print() | ^^^^^ E303 -395 | # end +407 | # end | = help: Remove extraneous blank line(s) ℹ Fix -390 390 | print() -391 391 | -392 392 | -393 |- -394 393 | print() -395 394 | # end -396 395 | +402 402 | print() +403 403 | +404 404 | +405 |- +406 405 | print() +407 406 | # end +408 407 | -E30.py:403:1: E303 [*] Too many blank lines (3) +E30.py:415:1: E303 [*] Too many blank lines (3) | -403 | # comment +415 | # comment | ^^^^^^^^^ E303 -404 | -405 | print() +416 | +417 | print() | = help: Remove extraneous blank line(s) ℹ Fix -399 399 | print() -400 400 | -401 401 | -402 |- -403 402 | # comment -404 403 | -405 404 | print() +411 411 | print() +412 412 | +413 413 | +414 |- +415 414 | # comment +416 415 | +417 416 | print() -E30.py:414:5: E303 [*] Too many blank lines (2) +E30.py:426:5: E303 [*] Too many blank lines (2) | -414 | # comment +426 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Fix -410 410 | def a(): -411 411 | print() -412 412 | -413 |- -414 413 | # comment -415 414 | -416 415 | +422 422 | def a(): +423 423 | print() +424 424 | +425 |- +426 425 | # comment +427 426 | +428 427 | -E30.py:417:5: E303 [*] Too many blank lines (2) +E30.py:429:5: E303 [*] Too many blank lines (2) | -417 | # another comment +429 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -418 | -419 | print() +430 | +431 | print() | = help: Remove extraneous blank line(s) ℹ Fix -413 413 | -414 414 | # comment -415 415 | -416 |- -417 416 | # another comment -418 417 | -419 418 | print() +425 425 | +426 426 | # comment +427 427 | +428 |- +429 428 | # another comment +430 429 | +431 430 | print() -E30.py:428:1: E303 [*] Too many blank lines (3) +E30.py:440:1: E303 [*] Too many blank lines (3) | -428 | / """This class docstring comes on line 5. -429 | | It gives error E303: too many blank lines (3) -430 | | """ +440 | / """This class docstring comes on line 5. +441 | | It gives error E303: too many blank lines (3) +442 | | """ | |___^ E303 -431 | # end +443 | # end | = help: Remove extraneous blank line(s) ℹ Fix -424 424 | #!python -425 425 | -426 426 | -427 |- -428 427 | """This class docstring comes on line 5. -429 428 | It gives error E303: too many blank lines (3) -430 429 | """ +436 436 | #!python +437 437 | +438 438 | +439 |- +440 439 | """This class docstring comes on line 5. +441 440 | It gives error E303: too many blank lines (3) +442 441 | """ diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 6744b896ca7e6..47fac3ac50088 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:437:1: E304 [*] blank lines found after function decorator +E30.py:449:1: E304 [*] blank lines found after function decorator | -435 | @decorator -436 | -437 | def function(): +447 | @decorator +448 | +449 | def function(): | ^^^ E304 -438 | pass -439 | # end +450 | pass +451 | # end | = help: Remove extraneous blank line(s) ℹ Fix -433 433 | -434 434 | # E304 -435 435 | @decorator -436 |- -437 436 | def function(): -438 437 | pass -439 438 | # end +445 445 | +446 446 | # E304 +447 447 | @decorator +448 |- +449 448 | def function(): +450 449 | pass +451 450 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 0b3c2e6cf0cdf..8f86e0b45488c 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:449:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:461:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -448 | # another comment -449 | fn() +460 | # another comment +461 | fn() | ^^ E305 -450 | # end +462 | # end | = help: Add missing blank line(s) ℹ Fix -446 446 | # comment -447 447 | -448 448 | # another comment - 449 |+ - 450 |+ -449 451 | fn() -450 452 | # end -451 453 | +458 458 | # comment +459 459 | +460 460 | # another comment + 461 |+ + 462 |+ +461 463 | fn() +462 464 | # end +463 465 | -E30.py:460:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:472:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -459 | # another comment -460 | a = 1 +471 | # another comment +472 | a = 1 | ^ E305 -461 | # end +473 | # end | = help: Add missing blank line(s) ℹ Fix -457 457 | # comment -458 458 | -459 459 | # another comment - 460 |+ - 461 |+ -460 462 | a = 1 -461 463 | # end -462 464 | +469 469 | # comment +470 470 | +471 471 | # another comment + 472 |+ + 473 |+ +472 474 | a = 1 +473 475 | # end +474 476 | -E30.py:472:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:484:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -470 | # another comment -471 | -472 | try: +482 | # another comment +483 | +484 | try: | ^^^ E305 -473 | fn() -474 | except Exception: +485 | fn() +486 | except Exception: | = help: Add missing blank line(s) ℹ Fix -469 469 | -470 470 | # another comment -471 471 | - 472 |+ -472 473 | try: -473 474 | fn() -474 475 | except Exception: +481 481 | +482 482 | # another comment +483 483 | + 484 |+ +484 485 | try: +485 486 | fn() +486 487 | except Exception: -E30.py:484:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:496:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -483 | # Two spaces before comments, too. -484 | if a(): +495 | # Two spaces before comments, too. +496 | if a(): | ^^ E305 -485 | a() -486 | # end +497 | a() +498 | # end | = help: Add missing blank line(s) ℹ Fix -481 481 | print -482 482 | -483 483 | # Two spaces before comments, too. - 484 |+ - 485 |+ -484 486 | if a(): -485 487 | a() -486 488 | # end +493 493 | print +494 494 | +495 495 | # Two spaces before comments, too. + 496 |+ + 497 |+ +496 498 | if a(): +497 499 | a() +498 500 | # end -E30.py:497:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:509:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -495 | blah, blah -496 | -497 | if __name__ == '__main__': +507 | blah, blah +508 | +509 | if __name__ == '__main__': | ^^ E305 -498 | main() -499 | # end +510 | main() +511 | # end | = help: Add missing blank line(s) ℹ Fix -494 494 | def main(): -495 495 | blah, blah -496 496 | - 497 |+ -497 498 | if __name__ == '__main__': -498 499 | main() -499 500 | # end +506 506 | def main(): +507 507 | blah, blah +508 508 | + 509 |+ +509 510 | if __name__ == '__main__': +510 511 | main() +511 512 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 3626275548dc4..40e9395b5d877 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:505:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:517:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -503 | def a(): -504 | x = 1 -505 | def b(): +515 | def a(): +516 | x = 1 +517 | def b(): | ^^^ E306 -506 | pass -507 | # end +518 | pass +519 | # end | = help: Add missing blank line ℹ Fix -502 502 | # E306:3:5 -503 503 | def a(): -504 504 | x = 1 - 505 |+ -505 506 | def b(): -506 507 | pass -507 508 | # end - -E30.py:513:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -511 | async def a(): -512 | x = 1 -513 | def b(): +514 514 | # E306:3:5 +515 515 | def a(): +516 516 | x = 1 + 517 |+ +517 518 | def b(): +518 519 | pass +519 520 | # end + +E30.py:525:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +523 | async def a(): +524 | x = 1 +525 | def b(): | ^^^ E306 -514 | pass -515 | # end +526 | pass +527 | # end | = help: Add missing blank line ℹ Fix -510 510 | #: E306:3:5 -511 511 | async def a(): -512 512 | x = 1 - 513 |+ -513 514 | def b(): -514 515 | pass -515 516 | # end - -E30.py:521:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -519 | def a(): -520 | x = 2 -521 | def b(): +522 522 | #: E306:3:5 +523 523 | async def a(): +524 524 | x = 1 + 525 |+ +525 526 | def b(): +526 527 | pass +527 528 | # end + +E30.py:533:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +531 | def a(): +532 | x = 2 +533 | def b(): | ^^^ E306 -522 | x = 1 -523 | def c(): +534 | x = 1 +535 | def c(): | = help: Add missing blank line ℹ Fix -518 518 | #: E306:3:5 E306:5:9 -519 519 | def a(): -520 520 | x = 2 - 521 |+ -521 522 | def b(): -522 523 | x = 1 -523 524 | def c(): - -E30.py:523:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -521 | def b(): -522 | x = 1 -523 | def c(): +530 530 | #: E306:3:5 E306:5:9 +531 531 | def a(): +532 532 | x = 2 + 533 |+ +533 534 | def b(): +534 535 | x = 1 +535 536 | def c(): + +E30.py:535:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +533 | def b(): +534 | x = 1 +535 | def c(): | ^^^ E306 -524 | pass -525 | # end +536 | pass +537 | # end | = help: Add missing blank line ℹ Fix -520 520 | x = 2 -521 521 | def b(): -522 522 | x = 1 - 523 |+ -523 524 | def c(): -524 525 | pass -525 526 | # end - -E30.py:531:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -529 | def a(): -530 | x = 1 -531 | class C: +532 532 | x = 2 +533 533 | def b(): +534 534 | x = 1 + 535 |+ +535 536 | def c(): +536 537 | pass +537 538 | # end + +E30.py:543:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +541 | def a(): +542 | x = 1 +543 | class C: | ^^^^^ E306 -532 | pass -533 | x = 2 +544 | pass +545 | x = 2 | = help: Add missing blank line ℹ Fix -528 528 | # E306:3:5 E306:6:5 -529 529 | def a(): -530 530 | x = 1 - 531 |+ -531 532 | class C: -532 533 | pass -533 534 | x = 2 - -E30.py:534:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -532 | pass -533 | x = 2 -534 | def b(): +540 540 | # E306:3:5 E306:6:5 +541 541 | def a(): +542 542 | x = 1 + 543 |+ +543 544 | class C: +544 545 | pass +545 546 | x = 2 + +E30.py:546:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +544 | pass +545 | x = 2 +546 | def b(): | ^^^ E306 -535 | pass -536 | # end +547 | pass +548 | # end | = help: Add missing blank line ℹ Fix -531 531 | class C: -532 532 | pass -533 533 | x = 2 - 534 |+ -534 535 | def b(): -535 536 | pass -536 537 | # end - -E30.py:543:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -541 | def bar(): -542 | pass -543 | def baz(): pass +543 543 | class C: +544 544 | pass +545 545 | x = 2 + 546 |+ +546 547 | def b(): +547 548 | pass +548 549 | # end + +E30.py:555:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +553 | def bar(): +554 | pass +555 | def baz(): pass | ^^^ E306 -544 | # end +556 | # end | = help: Add missing blank line ℹ Fix -540 540 | def foo(): -541 541 | def bar(): -542 542 | pass - 543 |+ -543 544 | def baz(): pass -544 545 | # end -545 546 | - -E30.py:550:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -548 | def foo(): -549 | def bar(): pass -550 | def baz(): +552 552 | def foo(): +553 553 | def bar(): +554 554 | pass + 555 |+ +555 556 | def baz(): pass +556 557 | # end +557 558 | + +E30.py:562:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +560 | def foo(): +561 | def bar(): pass +562 | def baz(): | ^^^ E306 -551 | pass -552 | # end +563 | pass +564 | # end | = help: Add missing blank line ℹ Fix -547 547 | # E306:3:5 -548 548 | def foo(): -549 549 | def bar(): pass - 550 |+ -550 551 | def baz(): -551 552 | pass -552 553 | # end +559 559 | # E306:3:5 +560 560 | def foo(): +561 561 | def bar(): pass + 562 |+ +562 563 | def baz(): +563 564 | pass +564 565 | # end From 2a7b4cb945b475b0c6feeb263a830cdd2860fa00 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 12 Nov 2023 23:53:48 +0900 Subject: [PATCH 012/122] Clippy fixes. --- .../rules/logical_lines/blank_lines.rs | 19 +++++-------------- .../pycodestyle/rules/logical_lines/mod.rs | 2 +- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index f543816d1a527..e1a8f87fec2a7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -351,15 +351,6 @@ pub(crate) fn blank_lines( stylist: &Stylist, context: &mut LogicalLinesContext, ) { - // dbg!( - // line.text(), - // &line.line.blank_lines, - // &line.line.preceding_blank_lines, - // indent_level, - // &tracked_vars - // ); - // dbg!(); - if tracked_vars.is_first_logical_line { if !line.is_comment_only() { tracked_vars.is_first_logical_line = false; @@ -372,7 +363,7 @@ pub(crate) fn blank_lines( if indent_level < tracked_vars.class_indent_level && tracked_vars.is_in_class { tracked_vars.is_in_class = false; if line.is_comment_only() { - tracked_vars.follows_comment_after_class = true + tracked_vars.follows_comment_after_class = true; } else { follows_class_or_fn = true; } @@ -381,7 +372,7 @@ pub(crate) fn blank_lines( if indent_level < tracked_vars.fn_indent_level && tracked_vars.is_in_fn { tracked_vars.is_in_fn = false; if line.is_comment_only() { - tracked_vars.follows_comment_after_fn = true + tracked_vars.follows_comment_after_fn = true; } else { follows_class_or_fn = true; } @@ -405,7 +396,7 @@ pub(crate) fn blank_lines( tracked_vars.follows_comment_after_class = false; } - for token in line.tokens().iter() { + for token in line.tokens() { if matches!(token.kind, TokenKind::Indent | TokenKind::Dedent) { continue; } @@ -415,7 +406,7 @@ pub(crate) fn blank_lines( && tracked_vars.is_in_class && ( // A comment before the def is allowed (as long as it is preceded by a blank line). - (line.line.preceding_blank_lines == 0 && line.line.blank_lines == 0 && prev_line.is_some_and(|line| line.is_comment_only())) + (line.line.preceding_blank_lines == 0 && line.line.blank_lines == 0 && prev_line.is_some_and(LogicalLine::is_comment_only)) // Standard case. || line.line.blank_lines == 0 && prev_line @@ -453,7 +444,7 @@ pub(crate) fn blank_lines( || tracked_vars.is_in_class || tracked_vars.is_in_fn // If a comment is preceding the def/class, the 2 blank lines can be before that comment. - || (line.line.preceding_blank_lines >= 2 && prev_line.map_or(false, |prev_line| prev_line.is_comment_only())) + || (line.line.preceding_blank_lines >= 2 && prev_line.map_or(false, LogicalLine::is_comment_only)) ) // Allow directly following an except. && prev_line diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs index ec58fa33127ab..e57d9aae6c496 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs @@ -488,7 +488,7 @@ impl LogicalLinesBuilder { self.current_blank_characters += end - self.current_line.tokens_start; } else { if self.previous_blank_lines < self.current_blank_lines { - self.previous_blank_lines = self.current_blank_lines + self.previous_blank_lines = self.current_blank_lines; } self.lines.push(Line { flags: self.current_line.flags, From aa7b6e6922a7b145f46ee1088d38c9ff1fdeb0dc Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Thu, 16 Nov 2023 21:33:44 +0900 Subject: [PATCH 013/122] Store line.is_comment_only()'s result in a variable. Use config more. --- .../rules/logical_lines/blank_lines.rs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index e1a8f87fec2a7..6be5c515e1322 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -340,6 +340,7 @@ fn is_decorator(line: Option<&LogicalLine>) -> bool { } /// E301, E302, E303, E304, E305, E306 +#[allow(clippy::too_many_arguments)] pub(crate) fn blank_lines( line: &LogicalLine, prev_line: Option<&LogicalLine>, @@ -351,8 +352,10 @@ pub(crate) fn blank_lines( stylist: &Stylist, context: &mut LogicalLinesContext, ) { + let line_is_comment_only = line.is_comment_only(); + if tracked_vars.is_first_logical_line { - if !line.is_comment_only() { + if !line_is_comment_only { tracked_vars.is_first_logical_line = false; } // Don't expect blank lines before the first line non comment line. @@ -362,7 +365,7 @@ pub(crate) fn blank_lines( let mut follows_class_or_fn = false; if indent_level < tracked_vars.class_indent_level && tracked_vars.is_in_class { tracked_vars.is_in_class = false; - if line.is_comment_only() { + if line_is_comment_only { tracked_vars.follows_comment_after_class = true; } else { follows_class_or_fn = true; @@ -371,14 +374,16 @@ pub(crate) fn blank_lines( if indent_level < tracked_vars.fn_indent_level && tracked_vars.is_in_fn { tracked_vars.is_in_fn = false; - if line.is_comment_only() { + if line_is_comment_only { tracked_vars.follows_comment_after_fn = true; } else { follows_class_or_fn = true; } } - if tracked_vars.follows_comment_after_fn && !line.is_comment_only() { + // A comment can be de-indented while still being in a class/function, in that case + // we need to revert the variables. + if tracked_vars.follows_comment_after_fn && !line_is_comment_only { if indent_level == tracked_vars.fn_indent_level { tracked_vars.is_in_fn = true; } else { @@ -387,7 +392,7 @@ pub(crate) fn blank_lines( tracked_vars.follows_comment_after_fn = false; } - if tracked_vars.follows_comment_after_class && !line.is_comment_only() { + if tracked_vars.follows_comment_after_class && !line_is_comment_only { if indent_level == tracked_vars.class_indent_level { tracked_vars.is_in_class = true; } else { @@ -422,7 +427,7 @@ pub(crate) fn blank_lines( // E301 let mut diagnostic = Diagnostic::new(BlankLineBetweenMethods(line.line.blank_lines), token.range); - // TODO: in the case where there is a comment between two methods, make the comment "stick" + // TODO: In the case where there is a comment between two methods, make the comment "stick" // to the second method instead of the first. diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().as_str().to_string(), @@ -444,13 +449,13 @@ pub(crate) fn blank_lines( || tracked_vars.is_in_class || tracked_vars.is_in_fn // If a comment is preceding the def/class, the 2 blank lines can be before that comment. - || (line.line.preceding_blank_lines >= 2 && prev_line.map_or(false, LogicalLine::is_comment_only)) + || (line.line.preceding_blank_lines >= BlankLinesConfig::TOP_LEVEL && prev_line.map_or(false, LogicalLine::is_comment_only)) ) // Allow directly following an except. && prev_line .and_then(|prev_line| prev_line.tokens_trimmed().first()) .map_or(true, |token| !matches!(token.kind(), TokenKind::Except)) - && line.line.blank_lines < 2 + && line.line.blank_lines < BlankLinesConfig::TOP_LEVEL && prev_line.is_some() { // E302 @@ -461,9 +466,10 @@ pub(crate) fn blank_lines( .line_ending() .as_str() .to_string() - .repeat(2 - line.line.blank_lines as usize), + .repeat((BlankLinesConfig::TOP_LEVEL - line.line.blank_lines) as usize), locator.line_start(token.range.start()), ))); + context.push_diagnostic(diagnostic); } else if line.line.blank_lines > BlankLinesConfig::TOP_LEVEL || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) @@ -493,11 +499,12 @@ pub(crate) fn blank_lines( - TextSize::new(line.line.preceding_blank_characters), locator.line_start(range.start()), ))); + context.push_diagnostic(diagnostic); } else if line.line.preceding_blank_lines < BlankLinesConfig::TOP_LEVEL && follows_class_or_fn && indent_level == 0 - && !line.is_comment_only() + && !line_is_comment_only && !matches!( token.kind(), TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async @@ -508,14 +515,16 @@ pub(crate) fn blank_lines( BlankLinesAfterFunctionOrClass(line.line.blank_lines), token.range, ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist .line_ending() .as_str() .to_string() - .repeat(2 - line.line.blank_lines as usize), + .repeat((BlankLinesConfig::TOP_LEVEL - line.line.blank_lines) as usize), locator.line_start(token.range.start()), ))); + context.push_diagnostic(diagnostic); } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class) && (tracked_vars.is_in_class || tracked_vars.is_in_fn) @@ -535,6 +544,7 @@ pub(crate) fn blank_lines( BlankLinesBeforeNestedDefinition(line.line.blank_lines), token.range, ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().as_str().to_string(), locator.line_start(token.range.start()), From 8d3744d58bd8bb56b6d1e2fb8bfd5e96ba9e7162 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Thu, 16 Nov 2023 22:33:35 +0900 Subject: [PATCH 014/122] Do not ignore first logical line when it comes to tracked vars. --- .../pycodestyle/rules/logical_lines/blank_lines.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 6be5c515e1322..499fc93924df1 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -354,12 +354,8 @@ pub(crate) fn blank_lines( ) { let line_is_comment_only = line.is_comment_only(); - if tracked_vars.is_first_logical_line { - if !line_is_comment_only { - tracked_vars.is_first_logical_line = false; - } - // Don't expect blank lines before the first line non comment line. - return; + if tracked_vars.is_first_logical_line && !line_is_comment_only { + tracked_vars.is_first_logical_line = false; } let mut follows_class_or_fn = false; @@ -406,7 +402,9 @@ pub(crate) fn blank_lines( continue; } - if token.kind() == TokenKind::Def + if tracked_vars.is_first_logical_line { + // Don't expect blank lines before the first line non comment line. + } else if token.kind() == TokenKind::Def // Only applies to method. && tracked_vars.is_in_class && ( From bd282ec60e0c25539d8260364c62a6c21ba58647 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Thu, 16 Nov 2023 22:52:35 +0900 Subject: [PATCH 015/122] Remove unnecessary check. --- .../src/rules/pycodestyle/rules/logical_lines/blank_lines.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 499fc93924df1..72447b2e66918 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -454,7 +454,6 @@ pub(crate) fn blank_lines( .and_then(|prev_line| prev_line.tokens_trimmed().first()) .map_or(true, |token| !matches!(token.kind(), TokenKind::Except)) && line.line.blank_lines < BlankLinesConfig::TOP_LEVEL - && prev_line.is_some() { // E302 let mut diagnostic = From 2ef4d02ad43bd500689ec3b2989ee2c34f7cf26a Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Thu, 16 Nov 2023 23:44:01 +0900 Subject: [PATCH 016/122] Update snapshots (Fix -> Safe fix) --- ...rules__pycodestyle__tests__E301_E30.py.snap | 4 ++-- ...rules__pycodestyle__tests__E302_E30.py.snap | 18 +++++++++--------- ...rules__pycodestyle__tests__E303_E30.py.snap | 14 +++++++------- ...rules__pycodestyle__tests__E304_E30.py.snap | 2 +- ...rules__pycodestyle__tests__E305_E30.py.snap | 10 +++++----- ...rules__pycodestyle__tests__E306_E30.py.snap | 16 ++++++++-------- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index e722fff7a4559..61b6bc57fbe5d 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -12,7 +12,7 @@ E30.py:285:5: E301 [*] Expected 1 blank line, found 0 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 282 282 | 283 283 | def func1(): 284 284 | pass @@ -32,7 +32,7 @@ E30.py:296:5: E301 [*] Expected 1 blank line, found 0 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 293 293 | def fn1(): 294 294 | pass 295 295 | # comment diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 590e595fe5ef5..64fbbbeaab5d6 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -12,7 +12,7 @@ E30.py:303:1: E302 [*] Expected 2 blank lines, found 0 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 300 300 | 301 301 | # E302 302 302 | """Main module.""" @@ -33,7 +33,7 @@ E30.py:310:1: E302 [*] Expected 2 blank lines, found 0 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 307 307 | 308 308 | # E302 309 309 | import sys @@ -54,7 +54,7 @@ E30.py:319:1: E302 [*] Expected 2 blank lines, found 1 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 316 316 | def a(): 317 317 | pass 318 318 | @@ -74,7 +74,7 @@ E30.py:330:1: E302 [*] Expected 2 blank lines, found 1 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 327 327 | 328 328 | # comment 329 329 | @@ -94,7 +94,7 @@ E30.py:339:7: E302 [*] Expected 2 blank lines, found 1 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 336 336 | def a(): 337 337 | pass 338 338 | @@ -114,7 +114,7 @@ E30.py:348:8: E302 [*] Expected 2 blank lines, found 1 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 345 345 | async def x(): 346 346 | pass 347 347 | @@ -133,7 +133,7 @@ E30.py:356:1: E302 [*] Expected 2 blank lines, found 0 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 353 353 | # E302 354 354 | def bar(): 355 355 | pass @@ -154,7 +154,7 @@ E30.py:362:1: E302 [*] Expected 2 blank lines, found 0 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 359 359 | 360 360 | # E302 361 361 | def bar(): pass @@ -174,7 +174,7 @@ E30.py:372:1: E302 [*] Expected 2 blank lines, found 0 | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 369 369 | pass 370 370 | 371 371 | # comment diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 22b8388bbb221..e1683bb31b080 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -10,7 +10,7 @@ E30.py:383:5: E303 [*] Too many blank lines (2) | = help: Remove extraneous blank line(s) -ℹ Fix +ℹ Safe fix 379 379 | def fn(): 380 380 | _ = None 381 381 | @@ -28,7 +28,7 @@ E30.py:395:5: E303 [*] Too many blank lines (2) | = help: Remove extraneous blank line(s) -ℹ Fix +ℹ Safe fix 391 391 | def fn(): 392 392 | _ = None 393 393 | @@ -45,7 +45,7 @@ E30.py:406:1: E303 [*] Too many blank lines (3) | = help: Remove extraneous blank line(s) -ℹ Fix +ℹ Safe fix 402 402 | print() 403 403 | 404 404 | @@ -63,7 +63,7 @@ E30.py:415:1: E303 [*] Too many blank lines (3) | = help: Remove extraneous blank line(s) -ℹ Fix +ℹ Safe fix 411 411 | print() 412 412 | 413 413 | @@ -79,7 +79,7 @@ E30.py:426:5: E303 [*] Too many blank lines (2) | = help: Remove extraneous blank line(s) -ℹ Fix +ℹ Safe fix 422 422 | def a(): 423 423 | print() 424 424 | @@ -97,7 +97,7 @@ E30.py:429:5: E303 [*] Too many blank lines (2) | = help: Remove extraneous blank line(s) -ℹ Fix +ℹ Safe fix 425 425 | 426 426 | # comment 427 427 | @@ -116,7 +116,7 @@ E30.py:440:1: E303 [*] Too many blank lines (3) | = help: Remove extraneous blank line(s) -ℹ Fix +ℹ Safe fix 436 436 | #!python 437 437 | 438 438 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 47fac3ac50088..f3935c6662ec8 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -12,7 +12,7 @@ E30.py:449:1: E304 [*] blank lines found after function decorator | = help: Remove extraneous blank line(s) -ℹ Fix +ℹ Safe fix 445 445 | 446 446 | # E304 447 447 | @decorator diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 8f86e0b45488c..69f8afddb3139 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -10,7 +10,7 @@ E30.py:461:1: E305 [*] expected 2 blank lines after class or function definition | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 458 458 | # comment 459 459 | 460 460 | # another comment @@ -29,7 +29,7 @@ E30.py:472:1: E305 [*] expected 2 blank lines after class or function definition | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 469 469 | # comment 470 470 | 471 471 | # another comment @@ -50,7 +50,7 @@ E30.py:484:1: E305 [*] expected 2 blank lines after class or function definition | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 481 481 | 482 482 | # another comment 483 483 | @@ -69,7 +69,7 @@ E30.py:496:1: E305 [*] expected 2 blank lines after class or function definition | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 493 493 | print 494 494 | 495 495 | # Two spaces before comments, too. @@ -90,7 +90,7 @@ E30.py:509:1: E305 [*] expected 2 blank lines after class or function definition | = help: Add missing blank line(s) -ℹ Fix +ℹ Safe fix 506 506 | def main(): 507 507 | blah, blah 508 508 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 40e9395b5d877..cc27969a980f3 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -12,7 +12,7 @@ E30.py:517:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | = help: Add missing blank line -ℹ Fix +ℹ Safe fix 514 514 | # E306:3:5 515 515 | def a(): 516 516 | x = 1 @@ -32,7 +32,7 @@ E30.py:525:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | = help: Add missing blank line -ℹ Fix +ℹ Safe fix 522 522 | #: E306:3:5 523 523 | async def a(): 524 524 | x = 1 @@ -52,7 +52,7 @@ E30.py:533:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | = help: Add missing blank line -ℹ Fix +ℹ Safe fix 530 530 | #: E306:3:5 E306:5:9 531 531 | def a(): 532 532 | x = 2 @@ -72,7 +72,7 @@ E30.py:535:9: E306 [*] Expected 1 blank line before a nested definition, found 0 | = help: Add missing blank line -ℹ Fix +ℹ Safe fix 532 532 | x = 2 533 533 | def b(): 534 534 | x = 1 @@ -92,7 +92,7 @@ E30.py:543:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | = help: Add missing blank line -ℹ Fix +ℹ Safe fix 540 540 | # E306:3:5 E306:6:5 541 541 | def a(): 542 542 | x = 1 @@ -112,7 +112,7 @@ E30.py:546:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | = help: Add missing blank line -ℹ Fix +ℹ Safe fix 543 543 | class C: 544 544 | pass 545 545 | x = 2 @@ -131,7 +131,7 @@ E30.py:555:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | = help: Add missing blank line -ℹ Fix +ℹ Safe fix 552 552 | def foo(): 553 553 | def bar(): 554 554 | pass @@ -151,7 +151,7 @@ E30.py:562:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | = help: Add missing blank line -ℹ Fix +ℹ Safe fix 559 559 | # E306:3:5 560 560 | def foo(): 561 561 | def bar(): pass From 36d2547a974b623345c04f63d42837d909191d54 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 17 Nov 2023 00:24:16 +0900 Subject: [PATCH 017/122] Fix first logical line issue. --- .../rules/pycodestyle/rules/logical_lines/blank_lines.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 72447b2e66918..b249673873ab4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -354,10 +354,6 @@ pub(crate) fn blank_lines( ) { let line_is_comment_only = line.is_comment_only(); - if tracked_vars.is_first_logical_line && !line_is_comment_only { - tracked_vars.is_first_logical_line = false; - } - let mut follows_class_or_fn = false; if indent_level < tracked_vars.class_indent_level && tracked_vars.is_in_class { tracked_vars.is_in_class = false; @@ -585,4 +581,8 @@ pub(crate) fn blank_lines( } } } + + if tracked_vars.is_first_logical_line && !line_is_comment_only { + tracked_vars.is_first_logical_line = false; + } } From 0f6101140cc8d7590eafd8c2670b309d2d8fdfcf Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 17 Nov 2023 00:25:47 +0900 Subject: [PATCH 018/122] Fix false positive caused by a class's docstring. --- .../test/fixtures/pycodestyle/E30.py | 8 + .../rules/logical_lines/blank_lines.rs | 14 + ...ules__pycodestyle__tests__E301_E30.py.snap | 44 +-- ...ules__pycodestyle__tests__E302_E30.py.snap | 268 +++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 150 +++++----- ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 ++++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 194 ++++++------- 8 files changed, 416 insertions(+), 414 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 1bad29a9bbe3d..97bc59c253718 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -11,6 +11,14 @@ class Class: # end +# No error +class Class: + """Docstring""" + def __init__(self) -> None: + pass +# end + + # No error def func(): pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index b249673873ab4..c5418c7df9f65 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -339,6 +339,16 @@ fn is_decorator(line: Option<&LogicalLine>) -> bool { .map_or(false, |token| matches!(token.kind(), TokenKind::At)) } +/// Check if the given line starts with a decorator. +/// Returns `true` if line is a docstring only line. +fn is_docstring(line: Option<&LogicalLine>) -> bool { + line.is_some_and(|line| { + line.tokens_trimmed() + .iter() + .all(|token| matches!(token.kind(), TokenKind::String)) + }) +} + /// E301, E302, E303, E304, E305, E306 #[allow(clippy::too_many_arguments)] pub(crate) fn blank_lines( @@ -403,6 +413,8 @@ pub(crate) fn blank_lines( } else if token.kind() == TokenKind::Def // Only applies to method. && tracked_vars.is_in_class + // The class's docstring can directly precede the first function. + && !is_docstring(prev_line) && ( // A comment before the def is allowed (as long as it is preceded by a blank line). (line.line.preceding_blank_lines == 0 && line.line.blank_lines == 0 && prev_line.is_some_and(LogicalLine::is_comment_only)) @@ -523,6 +535,8 @@ pub(crate) fn blank_lines( && (tracked_vars.is_in_class || tracked_vars.is_in_fn) && line.line.preceding_blank_lines == 0 && !is_decorator(prev_line) + // The class's docstring can directly precede the first function. + && !is_docstring(prev_line) && !prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level < indent_level) // Allow groups of one-liners. && !(tracked_vars.follows_def diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index 61b6bc57fbe5d..8b133641cb9ac 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:285:5: E301 [*] Expected 1 blank line, found 0 +E30.py:293:5: E301 [*] Expected 1 blank line, found 0 | -283 | def func1(): -284 | pass -285 | def func2(): +291 | def func1(): +292 | pass +293 | def func2(): | ^^^ E301 -286 | pass -287 | # end - | - = help: Add missing blank line(s) - -ℹ Safe fix -282 282 | -283 283 | def func1(): -284 284 | pass - 285 |+ -285 286 | def func2(): -286 287 | pass -287 288 | # end - -E30.py:296:5: E301 [*] Expected 1 blank line, found 0 - | 294 | pass -295 | # comment -296 | def fn2(): - | ^^^ E301 -297 | pass -298 | # end +295 | # end | = help: Add missing blank line(s) ℹ Safe fix -293 293 | def fn1(): -294 294 | pass -295 295 | # comment - 296 |+ -296 297 | def fn2(): -297 298 | pass -298 299 | # end +290 290 | +291 291 | def func1(): +292 292 | pass + 293 |+ +293 294 | def func2(): +294 295 | pass +295 296 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 64fbbbeaab5d6..827686cac25e2 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,187 +1,187 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:303:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:311:1: E302 [*] Expected 2 blank lines, found 0 | -301 | # E302 -302 | """Main module.""" -303 | def fn(): +309 | # E302 +310 | """Main module.""" +311 | def fn(): | ^^^ E302 -304 | pass -305 | # end +312 | pass +313 | # end | = help: Add missing blank line(s) ℹ Safe fix -300 300 | -301 301 | # E302 -302 302 | """Main module.""" - 303 |+ - 304 |+ -303 305 | def fn(): -304 306 | pass -305 307 | # end - -E30.py:310:1: E302 [*] Expected 2 blank lines, found 0 - | -308 | # E302 -309 | import sys -310 | def get_sys_path(): +308 308 | +309 309 | # E302 +310 310 | """Main module.""" + 311 |+ + 312 |+ +311 313 | def fn(): +312 314 | pass +313 315 | # end + +E30.py:318:1: E302 [*] Expected 2 blank lines, found 0 + | +316 | # E302 +317 | import sys +318 | def get_sys_path(): | ^^^ E302 -311 | return sys.path -312 | # end +319 | return sys.path +320 | # end | = help: Add missing blank line(s) ℹ Safe fix -307 307 | -308 308 | # E302 -309 309 | import sys - 310 |+ - 311 |+ -310 312 | def get_sys_path(): -311 313 | return sys.path -312 314 | # end +315 315 | +316 316 | # E302 +317 317 | import sys + 318 |+ + 319 |+ +318 320 | def get_sys_path(): +319 321 | return sys.path +320 322 | # end -E30.py:319:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:327:1: E302 [*] Expected 2 blank lines, found 1 | -317 | pass -318 | -319 | def b(): +325 | pass +326 | +327 | def b(): | ^^^ E302 -320 | pass -321 | # end +328 | pass +329 | # end | = help: Add missing blank line(s) ℹ Safe fix -316 316 | def a(): -317 317 | pass -318 318 | - 319 |+ -319 320 | def b(): -320 321 | pass -321 322 | # end - -E30.py:330:1: E302 [*] Expected 2 blank lines, found 1 - | -328 | # comment -329 | -330 | def b(): +324 324 | def a(): +325 325 | pass +326 326 | + 327 |+ +327 328 | def b(): +328 329 | pass +329 330 | # end + +E30.py:338:1: E302 [*] Expected 2 blank lines, found 1 + | +336 | # comment +337 | +338 | def b(): | ^^^ E302 -331 | pass -332 | # end +339 | pass +340 | # end | = help: Add missing blank line(s) ℹ Safe fix -327 327 | -328 328 | # comment -329 329 | - 330 |+ -330 331 | def b(): -331 332 | pass -332 333 | # end - -E30.py:339:7: E302 [*] Expected 2 blank lines, found 1 - | -337 | pass -338 | -339 | async def b(): +335 335 | +336 336 | # comment +337 337 | + 338 |+ +338 339 | def b(): +339 340 | pass +340 341 | # end + +E30.py:347:7: E302 [*] Expected 2 blank lines, found 1 + | +345 | pass +346 | +347 | async def b(): | ^^^ E302 -340 | pass -341 | # end +348 | pass +349 | # end | = help: Add missing blank line(s) ℹ Safe fix -336 336 | def a(): -337 337 | pass -338 338 | - 339 |+ -339 340 | async def b(): -340 341 | pass -341 342 | # end - -E30.py:348:8: E302 [*] Expected 2 blank lines, found 1 - | -346 | pass -347 | -348 | async def x(y: int = 1): +344 344 | def a(): +345 345 | pass +346 346 | + 347 |+ +347 348 | async def b(): +348 349 | pass +349 350 | # end + +E30.py:356:8: E302 [*] Expected 2 blank lines, found 1 + | +354 | pass +355 | +356 | async def x(y: int = 1): | ^^^ E302 -349 | pass -350 | # end +357 | pass +358 | # end | = help: Add missing blank line(s) ℹ Safe fix -345 345 | async def x(): -346 346 | pass -347 347 | - 348 |+ -348 349 | async def x(y: int = 1): -349 350 | pass -350 351 | # end - -E30.py:356:1: E302 [*] Expected 2 blank lines, found 0 - | -354 | def bar(): -355 | pass -356 | def baz(): pass +353 353 | async def x(): +354 354 | pass +355 355 | + 356 |+ +356 357 | async def x(y: int = 1): +357 358 | pass +358 359 | # end + +E30.py:364:1: E302 [*] Expected 2 blank lines, found 0 + | +362 | def bar(): +363 | pass +364 | def baz(): pass | ^^^ E302 -357 | # end +365 | # end | = help: Add missing blank line(s) ℹ Safe fix -353 353 | # E302 -354 354 | def bar(): -355 355 | pass - 356 |+ - 357 |+ -356 358 | def baz(): pass -357 359 | # end -358 360 | - -E30.py:362:1: E302 [*] Expected 2 blank lines, found 0 - | -360 | # E302 -361 | def bar(): pass -362 | def baz(): +361 361 | # E302 +362 362 | def bar(): +363 363 | pass + 364 |+ + 365 |+ +364 366 | def baz(): pass +365 367 | # end +366 368 | + +E30.py:370:1: E302 [*] Expected 2 blank lines, found 0 + | +368 | # E302 +369 | def bar(): pass +370 | def baz(): | ^^^ E302 -363 | pass -364 | # end +371 | pass +372 | # end | = help: Add missing blank line(s) ℹ Safe fix -359 359 | -360 360 | # E302 -361 361 | def bar(): pass - 362 |+ - 363 |+ -362 364 | def baz(): -363 365 | pass -364 366 | # end - -E30.py:372:1: E302 [*] Expected 2 blank lines, found 0 - | -371 | # comment -372 | @decorator +367 367 | +368 368 | # E302 +369 369 | def bar(): pass + 370 |+ + 371 |+ +370 372 | def baz(): +371 373 | pass +372 374 | # end + +E30.py:380:1: E302 [*] Expected 2 blank lines, found 0 + | +379 | # comment +380 | @decorator | ^ E302 -373 | def g(): -374 | pass +381 | def g(): +382 | pass | = help: Add missing blank line(s) ℹ Safe fix -369 369 | pass -370 370 | -371 371 | # comment - 372 |+ - 373 |+ -372 374 | @decorator -373 375 | def g(): -374 376 | pass +377 377 | pass +378 378 | +379 379 | # comment + 380 |+ + 381 |+ +380 382 | @decorator +381 383 | def g(): +382 384 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index e1683bb31b080..f146390a2bf47 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,128 +1,128 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:383:5: E303 [*] Too many blank lines (2) +E30.py:391:5: E303 [*] Too many blank lines (2) | -383 | # arbitrary comment +391 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -384 | -385 | def inner(): # E306 not expected +392 | +393 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -379 379 | def fn(): -380 380 | _ = None -381 381 | -382 |- -383 382 | # arbitrary comment -384 383 | -385 384 | def inner(): # E306 not expected +387 387 | def fn(): +388 388 | _ = None +389 389 | +390 |- +391 390 | # arbitrary comment +392 391 | +393 392 | def inner(): # E306 not expected -E30.py:395:5: E303 [*] Too many blank lines (2) +E30.py:403:5: E303 [*] Too many blank lines (2) | -395 | # arbitrary comment +403 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -396 | def inner(): # E306 not expected -397 | pass +404 | def inner(): # E306 not expected +405 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -391 391 | def fn(): -392 392 | _ = None -393 393 | -394 |- -395 394 | # arbitrary comment -396 395 | def inner(): # E306 not expected -397 396 | pass +399 399 | def fn(): +400 400 | _ = None +401 401 | +402 |- +403 402 | # arbitrary comment +404 403 | def inner(): # E306 not expected +405 404 | pass -E30.py:406:1: E303 [*] Too many blank lines (3) +E30.py:414:1: E303 [*] Too many blank lines (3) | -406 | print() +414 | print() | ^^^^^ E303 -407 | # end +415 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -402 402 | print() -403 403 | -404 404 | -405 |- -406 405 | print() -407 406 | # end -408 407 | +410 410 | print() +411 411 | +412 412 | +413 |- +414 413 | print() +415 414 | # end +416 415 | -E30.py:415:1: E303 [*] Too many blank lines (3) +E30.py:423:1: E303 [*] Too many blank lines (3) | -415 | # comment +423 | # comment | ^^^^^^^^^ E303 -416 | -417 | print() +424 | +425 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -411 411 | print() -412 412 | -413 413 | -414 |- -415 414 | # comment -416 415 | -417 416 | print() +419 419 | print() +420 420 | +421 421 | +422 |- +423 422 | # comment +424 423 | +425 424 | print() -E30.py:426:5: E303 [*] Too many blank lines (2) +E30.py:434:5: E303 [*] Too many blank lines (2) | -426 | # comment +434 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -422 422 | def a(): -423 423 | print() -424 424 | -425 |- -426 425 | # comment -427 426 | -428 427 | +430 430 | def a(): +431 431 | print() +432 432 | +433 |- +434 433 | # comment +435 434 | +436 435 | -E30.py:429:5: E303 [*] Too many blank lines (2) +E30.py:437:5: E303 [*] Too many blank lines (2) | -429 | # another comment +437 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -430 | -431 | print() +438 | +439 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -425 425 | -426 426 | # comment -427 427 | -428 |- -429 428 | # another comment -430 429 | -431 430 | print() +433 433 | +434 434 | # comment +435 435 | +436 |- +437 436 | # another comment +438 437 | +439 438 | print() -E30.py:440:1: E303 [*] Too many blank lines (3) +E30.py:448:1: E303 [*] Too many blank lines (3) | -440 | / """This class docstring comes on line 5. -441 | | It gives error E303: too many blank lines (3) -442 | | """ +448 | / """This class docstring comes on line 5. +449 | | It gives error E303: too many blank lines (3) +450 | | """ | |___^ E303 -443 | # end +451 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -436 436 | #!python -437 437 | -438 438 | -439 |- -440 439 | """This class docstring comes on line 5. -441 440 | It gives error E303: too many blank lines (3) -442 441 | """ +444 444 | #!python +445 445 | +446 446 | +447 |- +448 447 | """This class docstring comes on line 5. +449 448 | It gives error E303: too many blank lines (3) +450 449 | """ diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index f3935c6662ec8..4d3925b4920ad 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:449:1: E304 [*] blank lines found after function decorator +E30.py:457:1: E304 [*] blank lines found after function decorator | -447 | @decorator -448 | -449 | def function(): +455 | @decorator +456 | +457 | def function(): | ^^^ E304 -450 | pass -451 | # end +458 | pass +459 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -445 445 | -446 446 | # E304 -447 447 | @decorator -448 |- -449 448 | def function(): -450 449 | pass -451 450 | # end +453 453 | +454 454 | # E304 +455 455 | @decorator +456 |- +457 456 | def function(): +458 457 | pass +459 458 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 69f8afddb3139..a43cff7f626ba 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:461:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:469:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -460 | # another comment -461 | fn() +468 | # another comment +469 | fn() | ^^ E305 -462 | # end +470 | # end | = help: Add missing blank line(s) ℹ Safe fix -458 458 | # comment -459 459 | -460 460 | # another comment - 461 |+ - 462 |+ -461 463 | fn() -462 464 | # end -463 465 | +466 466 | # comment +467 467 | +468 468 | # another comment + 469 |+ + 470 |+ +469 471 | fn() +470 472 | # end +471 473 | -E30.py:472:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:480:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -471 | # another comment -472 | a = 1 +479 | # another comment +480 | a = 1 | ^ E305 -473 | # end +481 | # end | = help: Add missing blank line(s) ℹ Safe fix -469 469 | # comment -470 470 | -471 471 | # another comment - 472 |+ - 473 |+ -472 474 | a = 1 -473 475 | # end -474 476 | +477 477 | # comment +478 478 | +479 479 | # another comment + 480 |+ + 481 |+ +480 482 | a = 1 +481 483 | # end +482 484 | -E30.py:484:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:492:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -482 | # another comment -483 | -484 | try: +490 | # another comment +491 | +492 | try: | ^^^ E305 -485 | fn() -486 | except Exception: +493 | fn() +494 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -481 481 | -482 482 | # another comment -483 483 | - 484 |+ -484 485 | try: -485 486 | fn() -486 487 | except Exception: +489 489 | +490 490 | # another comment +491 491 | + 492 |+ +492 493 | try: +493 494 | fn() +494 495 | except Exception: -E30.py:496:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:504:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -495 | # Two spaces before comments, too. -496 | if a(): +503 | # Two spaces before comments, too. +504 | if a(): | ^^ E305 -497 | a() -498 | # end +505 | a() +506 | # end | = help: Add missing blank line(s) ℹ Safe fix -493 493 | print -494 494 | -495 495 | # Two spaces before comments, too. - 496 |+ - 497 |+ -496 498 | if a(): -497 499 | a() -498 500 | # end +501 501 | print +502 502 | +503 503 | # Two spaces before comments, too. + 504 |+ + 505 |+ +504 506 | if a(): +505 507 | a() +506 508 | # end -E30.py:509:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:517:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -507 | blah, blah -508 | -509 | if __name__ == '__main__': +515 | blah, blah +516 | +517 | if __name__ == '__main__': | ^^ E305 -510 | main() -511 | # end +518 | main() +519 | # end | = help: Add missing blank line(s) ℹ Safe fix -506 506 | def main(): -507 507 | blah, blah -508 508 | - 509 |+ -509 510 | if __name__ == '__main__': -510 511 | main() -511 512 | # end +514 514 | def main(): +515 515 | blah, blah +516 516 | + 517 |+ +517 518 | if __name__ == '__main__': +518 519 | main() +519 520 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index cc27969a980f3..84603d534e66f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,29 +1,9 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:517:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -515 | def a(): -516 | x = 1 -517 | def b(): - | ^^^ E306 -518 | pass -519 | # end - | - = help: Add missing blank line - -ℹ Safe fix -514 514 | # E306:3:5 -515 515 | def a(): -516 516 | x = 1 - 517 |+ -517 518 | def b(): -518 519 | pass -519 520 | # end - E30.py:525:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -523 | async def a(): +523 | def a(): 524 | x = 1 525 | def b(): | ^^^ E306 @@ -33,8 +13,8 @@ E30.py:525:5: E306 [*] Expected 1 blank line before a nested definition, found 0 = help: Add missing blank line ℹ Safe fix -522 522 | #: E306:3:5 -523 523 | async def a(): +522 522 | # E306:3:5 +523 523 | def a(): 524 524 | x = 1 525 |+ 525 526 | def b(): @@ -43,121 +23,141 @@ E30.py:525:5: E306 [*] Expected 1 blank line before a nested definition, found 0 E30.py:533:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -531 | def a(): -532 | x = 2 +531 | async def a(): +532 | x = 1 533 | def b(): | ^^^ E306 -534 | x = 1 -535 | def c(): +534 | pass +535 | # end | = help: Add missing blank line ℹ Safe fix -530 530 | #: E306:3:5 E306:5:9 -531 531 | def a(): -532 532 | x = 2 +530 530 | #: E306:3:5 +531 531 | async def a(): +532 532 | x = 1 533 |+ 533 534 | def b(): -534 535 | x = 1 -535 536 | def c(): +534 535 | pass +535 536 | # end -E30.py:535:9: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:541:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -533 | def b(): -534 | x = 1 -535 | def c(): - | ^^^ E306 -536 | pass -537 | # end +539 | def a(): +540 | x = 2 +541 | def b(): + | ^^^ E306 +542 | x = 1 +543 | def c(): | = help: Add missing blank line ℹ Safe fix -532 532 | x = 2 -533 533 | def b(): -534 534 | x = 1 - 535 |+ -535 536 | def c(): -536 537 | pass -537 538 | # end - -E30.py:543:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -541 | def a(): -542 | x = 1 -543 | class C: - | ^^^^^ E306 -544 | pass -545 | x = 2 +538 538 | #: E306:3:5 E306:5:9 +539 539 | def a(): +540 540 | x = 2 + 541 |+ +541 542 | def b(): +542 543 | x = 1 +543 544 | def c(): + +E30.py:543:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +541 | def b(): +542 | x = 1 +543 | def c(): + | ^^^ E306 +544 | pass +545 | # end | = help: Add missing blank line ℹ Safe fix -540 540 | # E306:3:5 E306:6:5 -541 541 | def a(): -542 542 | x = 1 +540 540 | x = 2 +541 541 | def b(): +542 542 | x = 1 543 |+ -543 544 | class C: -544 545 | pass -545 546 | x = 2 +543 544 | def c(): +544 545 | pass +545 546 | # end -E30.py:546:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:551:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -544 | pass -545 | x = 2 -546 | def b(): - | ^^^ E306 -547 | pass -548 | # end +549 | def a(): +550 | x = 1 +551 | class C: + | ^^^^^ E306 +552 | pass +553 | x = 2 | = help: Add missing blank line ℹ Safe fix -543 543 | class C: -544 544 | pass -545 545 | x = 2 - 546 |+ -546 547 | def b(): -547 548 | pass -548 549 | # end - -E30.py:555:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -553 | def bar(): -554 | pass -555 | def baz(): pass +548 548 | # E306:3:5 E306:6:5 +549 549 | def a(): +550 550 | x = 1 + 551 |+ +551 552 | class C: +552 553 | pass +553 554 | x = 2 + +E30.py:554:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +552 | pass +553 | x = 2 +554 | def b(): | ^^^ E306 +555 | pass 556 | # end | = help: Add missing blank line ℹ Safe fix -552 552 | def foo(): -553 553 | def bar(): -554 554 | pass - 555 |+ -555 556 | def baz(): pass +551 551 | class C: +552 552 | pass +553 553 | x = 2 + 554 |+ +554 555 | def b(): +555 556 | pass 556 557 | # end -557 558 | -E30.py:562:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:563:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -560 | def foo(): -561 | def bar(): pass -562 | def baz(): +561 | def bar(): +562 | pass +563 | def baz(): pass | ^^^ E306 -563 | pass 564 | # end | = help: Add missing blank line ℹ Safe fix -559 559 | # E306:3:5 560 560 | def foo(): -561 561 | def bar(): pass - 562 |+ -562 563 | def baz(): -563 564 | pass +561 561 | def bar(): +562 562 | pass + 563 |+ +563 564 | def baz(): pass 564 565 | # end +565 566 | + +E30.py:570:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +568 | def foo(): +569 | def bar(): pass +570 | def baz(): + | ^^^ E306 +571 | pass +572 | # end + | + = help: Add missing blank line + +ℹ Safe fix +567 567 | # E306:3:5 +568 568 | def foo(): +569 569 | def bar(): pass + 570 |+ +570 571 | def baz(): +571 572 | pass +572 573 | # end From 0e9ca4ea8848309f11104cbdf74c730e9b369ed4 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 17 Nov 2023 00:53:52 +0900 Subject: [PATCH 019/122] Only trigger E302 on non-indented classes/functions. --- .../test/fixtures/pycodestyle/E30.py | 17 ++ .../rules/logical_lines/blank_lines.rs | 10 +- ...ules__pycodestyle__tests__E301_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E302_E30.py.snap | 268 +++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 150 +++++----- ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 ++++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 234 +++++++-------- 8 files changed, 438 insertions(+), 419 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 97bc59c253718..38b54ac00ac6a 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -285,6 +285,23 @@ def f( # end +# no error +if True: + class Class: + """Docstring""" + + def function(self): + ... +# end + + +# no error +if True: + def function(self): + ... +# end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index c5418c7df9f65..c614ed49262a9 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -452,12 +452,14 @@ pub(crate) fn blank_lines( .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) ) // Only apply to top level functions. - || tracked_vars.is_in_class - || tracked_vars.is_in_fn + || tracked_vars.is_in_class + || tracked_vars.is_in_fn // If a comment is preceding the def/class, the 2 blank lines can be before that comment. - || (line.line.preceding_blank_lines >= BlankLinesConfig::TOP_LEVEL && prev_line.map_or(false, LogicalLine::is_comment_only)) + || (line.line.preceding_blank_lines >= BlankLinesConfig::TOP_LEVEL && prev_line.map_or(false, LogicalLine::is_comment_only)) ) - // Allow directly following an except. + // Only trigger on non-indented classes and functions (for example functions within an if are ignored) + && indent_level == 0 + // Allow directly following an except. && prev_line .and_then(|prev_line| prev_line.tokens_trimmed().first()) .map_or(true, |token| !matches!(token.kind(), TokenKind::Except)) diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index 8b133641cb9ac..20ea2c959c2e3 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:293:5: E301 [*] Expected 1 blank line, found 0 +E30.py:310:5: E301 [*] Expected 1 blank line, found 0 | -291 | def func1(): -292 | pass -293 | def func2(): +308 | def func1(): +309 | pass +310 | def func2(): | ^^^ E301 -294 | pass -295 | # end +311 | pass +312 | # end | = help: Add missing blank line(s) ℹ Safe fix -290 290 | -291 291 | def func1(): -292 292 | pass - 293 |+ -293 294 | def func2(): -294 295 | pass -295 296 | # end +307 307 | +308 308 | def func1(): +309 309 | pass + 310 |+ +310 311 | def func2(): +311 312 | pass +312 313 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 827686cac25e2..4da9eb2f8b6d2 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,187 +1,187 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:311:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:328:1: E302 [*] Expected 2 blank lines, found 0 | -309 | # E302 -310 | """Main module.""" -311 | def fn(): +326 | # E302 +327 | """Main module.""" +328 | def fn(): | ^^^ E302 -312 | pass -313 | # end +329 | pass +330 | # end | = help: Add missing blank line(s) ℹ Safe fix -308 308 | -309 309 | # E302 -310 310 | """Main module.""" - 311 |+ - 312 |+ -311 313 | def fn(): -312 314 | pass -313 315 | # end - -E30.py:318:1: E302 [*] Expected 2 blank lines, found 0 - | -316 | # E302 -317 | import sys -318 | def get_sys_path(): +325 325 | +326 326 | # E302 +327 327 | """Main module.""" + 328 |+ + 329 |+ +328 330 | def fn(): +329 331 | pass +330 332 | # end + +E30.py:335:1: E302 [*] Expected 2 blank lines, found 0 + | +333 | # E302 +334 | import sys +335 | def get_sys_path(): | ^^^ E302 -319 | return sys.path -320 | # end +336 | return sys.path +337 | # end | = help: Add missing blank line(s) ℹ Safe fix -315 315 | -316 316 | # E302 -317 317 | import sys - 318 |+ - 319 |+ -318 320 | def get_sys_path(): -319 321 | return sys.path -320 322 | # end - -E30.py:327:1: E302 [*] Expected 2 blank lines, found 1 - | -325 | pass -326 | -327 | def b(): +332 332 | +333 333 | # E302 +334 334 | import sys + 335 |+ + 336 |+ +335 337 | def get_sys_path(): +336 338 | return sys.path +337 339 | # end + +E30.py:344:1: E302 [*] Expected 2 blank lines, found 1 + | +342 | pass +343 | +344 | def b(): | ^^^ E302 -328 | pass -329 | # end +345 | pass +346 | # end | = help: Add missing blank line(s) ℹ Safe fix -324 324 | def a(): -325 325 | pass -326 326 | - 327 |+ -327 328 | def b(): -328 329 | pass -329 330 | # end - -E30.py:338:1: E302 [*] Expected 2 blank lines, found 1 - | -336 | # comment -337 | -338 | def b(): +341 341 | def a(): +342 342 | pass +343 343 | + 344 |+ +344 345 | def b(): +345 346 | pass +346 347 | # end + +E30.py:355:1: E302 [*] Expected 2 blank lines, found 1 + | +353 | # comment +354 | +355 | def b(): | ^^^ E302 -339 | pass -340 | # end +356 | pass +357 | # end | = help: Add missing blank line(s) ℹ Safe fix -335 335 | -336 336 | # comment -337 337 | - 338 |+ -338 339 | def b(): -339 340 | pass -340 341 | # end - -E30.py:347:7: E302 [*] Expected 2 blank lines, found 1 - | -345 | pass -346 | -347 | async def b(): +352 352 | +353 353 | # comment +354 354 | + 355 |+ +355 356 | def b(): +356 357 | pass +357 358 | # end + +E30.py:364:7: E302 [*] Expected 2 blank lines, found 1 + | +362 | pass +363 | +364 | async def b(): | ^^^ E302 -348 | pass -349 | # end +365 | pass +366 | # end | = help: Add missing blank line(s) ℹ Safe fix -344 344 | def a(): -345 345 | pass -346 346 | - 347 |+ -347 348 | async def b(): -348 349 | pass -349 350 | # end - -E30.py:356:8: E302 [*] Expected 2 blank lines, found 1 - | -354 | pass -355 | -356 | async def x(y: int = 1): +361 361 | def a(): +362 362 | pass +363 363 | + 364 |+ +364 365 | async def b(): +365 366 | pass +366 367 | # end + +E30.py:373:8: E302 [*] Expected 2 blank lines, found 1 + | +371 | pass +372 | +373 | async def x(y: int = 1): | ^^^ E302 -357 | pass -358 | # end +374 | pass +375 | # end | = help: Add missing blank line(s) ℹ Safe fix -353 353 | async def x(): -354 354 | pass -355 355 | - 356 |+ -356 357 | async def x(y: int = 1): -357 358 | pass -358 359 | # end - -E30.py:364:1: E302 [*] Expected 2 blank lines, found 0 - | -362 | def bar(): -363 | pass -364 | def baz(): pass +370 370 | async def x(): +371 371 | pass +372 372 | + 373 |+ +373 374 | async def x(y: int = 1): +374 375 | pass +375 376 | # end + +E30.py:381:1: E302 [*] Expected 2 blank lines, found 0 + | +379 | def bar(): +380 | pass +381 | def baz(): pass | ^^^ E302 -365 | # end +382 | # end | = help: Add missing blank line(s) ℹ Safe fix -361 361 | # E302 -362 362 | def bar(): -363 363 | pass - 364 |+ - 365 |+ -364 366 | def baz(): pass -365 367 | # end -366 368 | +378 378 | # E302 +379 379 | def bar(): +380 380 | pass + 381 |+ + 382 |+ +381 383 | def baz(): pass +382 384 | # end +383 385 | -E30.py:370:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:387:1: E302 [*] Expected 2 blank lines, found 0 | -368 | # E302 -369 | def bar(): pass -370 | def baz(): +385 | # E302 +386 | def bar(): pass +387 | def baz(): | ^^^ E302 -371 | pass -372 | # end +388 | pass +389 | # end | = help: Add missing blank line(s) ℹ Safe fix -367 367 | -368 368 | # E302 -369 369 | def bar(): pass - 370 |+ - 371 |+ -370 372 | def baz(): -371 373 | pass -372 374 | # end - -E30.py:380:1: E302 [*] Expected 2 blank lines, found 0 - | -379 | # comment -380 | @decorator +384 384 | +385 385 | # E302 +386 386 | def bar(): pass + 387 |+ + 388 |+ +387 389 | def baz(): +388 390 | pass +389 391 | # end + +E30.py:397:1: E302 [*] Expected 2 blank lines, found 0 + | +396 | # comment +397 | @decorator | ^ E302 -381 | def g(): -382 | pass +398 | def g(): +399 | pass | = help: Add missing blank line(s) ℹ Safe fix -377 377 | pass -378 378 | -379 379 | # comment - 380 |+ - 381 |+ -380 382 | @decorator -381 383 | def g(): -382 384 | pass +394 394 | pass +395 395 | +396 396 | # comment + 397 |+ + 398 |+ +397 399 | @decorator +398 400 | def g(): +399 401 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index f146390a2bf47..68f44ac931792 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,128 +1,128 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:391:5: E303 [*] Too many blank lines (2) +E30.py:408:5: E303 [*] Too many blank lines (2) | -391 | # arbitrary comment +408 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -392 | -393 | def inner(): # E306 not expected +409 | +410 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -387 387 | def fn(): -388 388 | _ = None -389 389 | -390 |- -391 390 | # arbitrary comment -392 391 | -393 392 | def inner(): # E306 not expected +404 404 | def fn(): +405 405 | _ = None +406 406 | +407 |- +408 407 | # arbitrary comment +409 408 | +410 409 | def inner(): # E306 not expected -E30.py:403:5: E303 [*] Too many blank lines (2) +E30.py:420:5: E303 [*] Too many blank lines (2) | -403 | # arbitrary comment +420 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -404 | def inner(): # E306 not expected -405 | pass +421 | def inner(): # E306 not expected +422 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -399 399 | def fn(): -400 400 | _ = None -401 401 | -402 |- -403 402 | # arbitrary comment -404 403 | def inner(): # E306 not expected -405 404 | pass +416 416 | def fn(): +417 417 | _ = None +418 418 | +419 |- +420 419 | # arbitrary comment +421 420 | def inner(): # E306 not expected +422 421 | pass -E30.py:414:1: E303 [*] Too many blank lines (3) +E30.py:431:1: E303 [*] Too many blank lines (3) | -414 | print() +431 | print() | ^^^^^ E303 -415 | # end +432 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -410 410 | print() -411 411 | -412 412 | -413 |- -414 413 | print() -415 414 | # end -416 415 | +427 427 | print() +428 428 | +429 429 | +430 |- +431 430 | print() +432 431 | # end +433 432 | -E30.py:423:1: E303 [*] Too many blank lines (3) +E30.py:440:1: E303 [*] Too many blank lines (3) | -423 | # comment +440 | # comment | ^^^^^^^^^ E303 -424 | -425 | print() +441 | +442 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -419 419 | print() -420 420 | -421 421 | -422 |- -423 422 | # comment -424 423 | -425 424 | print() +436 436 | print() +437 437 | +438 438 | +439 |- +440 439 | # comment +441 440 | +442 441 | print() -E30.py:434:5: E303 [*] Too many blank lines (2) +E30.py:451:5: E303 [*] Too many blank lines (2) | -434 | # comment +451 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -430 430 | def a(): -431 431 | print() -432 432 | -433 |- -434 433 | # comment -435 434 | -436 435 | +447 447 | def a(): +448 448 | print() +449 449 | +450 |- +451 450 | # comment +452 451 | +453 452 | -E30.py:437:5: E303 [*] Too many blank lines (2) +E30.py:454:5: E303 [*] Too many blank lines (2) | -437 | # another comment +454 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -438 | -439 | print() +455 | +456 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -433 433 | -434 434 | # comment -435 435 | -436 |- -437 436 | # another comment -438 437 | -439 438 | print() +450 450 | +451 451 | # comment +452 452 | +453 |- +454 453 | # another comment +455 454 | +456 455 | print() -E30.py:448:1: E303 [*] Too many blank lines (3) +E30.py:465:1: E303 [*] Too many blank lines (3) | -448 | / """This class docstring comes on line 5. -449 | | It gives error E303: too many blank lines (3) -450 | | """ +465 | / """This class docstring comes on line 5. +466 | | It gives error E303: too many blank lines (3) +467 | | """ | |___^ E303 -451 | # end +468 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -444 444 | #!python -445 445 | -446 446 | -447 |- -448 447 | """This class docstring comes on line 5. -449 448 | It gives error E303: too many blank lines (3) -450 449 | """ +461 461 | #!python +462 462 | +463 463 | +464 |- +465 464 | """This class docstring comes on line 5. +466 465 | It gives error E303: too many blank lines (3) +467 466 | """ diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 4d3925b4920ad..f8dd02af6ad24 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:457:1: E304 [*] blank lines found after function decorator +E30.py:474:1: E304 [*] blank lines found after function decorator | -455 | @decorator -456 | -457 | def function(): +472 | @decorator +473 | +474 | def function(): | ^^^ E304 -458 | pass -459 | # end +475 | pass +476 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -453 453 | -454 454 | # E304 -455 455 | @decorator -456 |- -457 456 | def function(): -458 457 | pass -459 458 | # end +470 470 | +471 471 | # E304 +472 472 | @decorator +473 |- +474 473 | def function(): +475 474 | pass +476 475 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index a43cff7f626ba..695ecbdc524c3 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:469:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:486:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -468 | # another comment -469 | fn() +485 | # another comment +486 | fn() | ^^ E305 -470 | # end +487 | # end | = help: Add missing blank line(s) ℹ Safe fix -466 466 | # comment -467 467 | -468 468 | # another comment - 469 |+ - 470 |+ -469 471 | fn() -470 472 | # end -471 473 | +483 483 | # comment +484 484 | +485 485 | # another comment + 486 |+ + 487 |+ +486 488 | fn() +487 489 | # end +488 490 | -E30.py:480:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:497:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -479 | # another comment -480 | a = 1 +496 | # another comment +497 | a = 1 | ^ E305 -481 | # end +498 | # end | = help: Add missing blank line(s) ℹ Safe fix -477 477 | # comment -478 478 | -479 479 | # another comment - 480 |+ - 481 |+ -480 482 | a = 1 -481 483 | # end -482 484 | +494 494 | # comment +495 495 | +496 496 | # another comment + 497 |+ + 498 |+ +497 499 | a = 1 +498 500 | # end +499 501 | -E30.py:492:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:509:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -490 | # another comment -491 | -492 | try: +507 | # another comment +508 | +509 | try: | ^^^ E305 -493 | fn() -494 | except Exception: +510 | fn() +511 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -489 489 | -490 490 | # another comment -491 491 | - 492 |+ -492 493 | try: -493 494 | fn() -494 495 | except Exception: +506 506 | +507 507 | # another comment +508 508 | + 509 |+ +509 510 | try: +510 511 | fn() +511 512 | except Exception: -E30.py:504:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:521:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -503 | # Two spaces before comments, too. -504 | if a(): +520 | # Two spaces before comments, too. +521 | if a(): | ^^ E305 -505 | a() -506 | # end +522 | a() +523 | # end | = help: Add missing blank line(s) ℹ Safe fix -501 501 | print -502 502 | -503 503 | # Two spaces before comments, too. - 504 |+ - 505 |+ -504 506 | if a(): -505 507 | a() -506 508 | # end +518 518 | print +519 519 | +520 520 | # Two spaces before comments, too. + 521 |+ + 522 |+ +521 523 | if a(): +522 524 | a() +523 525 | # end -E30.py:517:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:534:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -515 | blah, blah -516 | -517 | if __name__ == '__main__': +532 | blah, blah +533 | +534 | if __name__ == '__main__': | ^^ E305 -518 | main() -519 | # end +535 | main() +536 | # end | = help: Add missing blank line(s) ℹ Safe fix -514 514 | def main(): -515 515 | blah, blah -516 516 | - 517 |+ -517 518 | if __name__ == '__main__': -518 519 | main() -519 520 | # end +531 531 | def main(): +532 532 | blah, blah +533 533 | + 534 |+ +534 535 | if __name__ == '__main__': +535 536 | main() +536 537 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 84603d534e66f..4029c27c5056f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:525:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:542:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -523 | def a(): -524 | x = 1 -525 | def b(): +540 | def a(): +541 | x = 1 +542 | def b(): | ^^^ E306 -526 | pass -527 | # end +543 | pass +544 | # end | = help: Add missing blank line ℹ Safe fix -522 522 | # E306:3:5 -523 523 | def a(): -524 524 | x = 1 - 525 |+ -525 526 | def b(): -526 527 | pass -527 528 | # end - -E30.py:533:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -531 | async def a(): -532 | x = 1 -533 | def b(): +539 539 | # E306:3:5 +540 540 | def a(): +541 541 | x = 1 + 542 |+ +542 543 | def b(): +543 544 | pass +544 545 | # end + +E30.py:550:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +548 | async def a(): +549 | x = 1 +550 | def b(): | ^^^ E306 -534 | pass -535 | # end +551 | pass +552 | # end | = help: Add missing blank line ℹ Safe fix -530 530 | #: E306:3:5 -531 531 | async def a(): -532 532 | x = 1 - 533 |+ -533 534 | def b(): -534 535 | pass -535 536 | # end - -E30.py:541:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -539 | def a(): -540 | x = 2 -541 | def b(): +547 547 | #: E306:3:5 +548 548 | async def a(): +549 549 | x = 1 + 550 |+ +550 551 | def b(): +551 552 | pass +552 553 | # end + +E30.py:558:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +556 | def a(): +557 | x = 2 +558 | def b(): | ^^^ E306 -542 | x = 1 -543 | def c(): +559 | x = 1 +560 | def c(): | = help: Add missing blank line ℹ Safe fix -538 538 | #: E306:3:5 E306:5:9 -539 539 | def a(): -540 540 | x = 2 - 541 |+ -541 542 | def b(): -542 543 | x = 1 -543 544 | def c(): - -E30.py:543:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -541 | def b(): -542 | x = 1 -543 | def c(): +555 555 | #: E306:3:5 E306:5:9 +556 556 | def a(): +557 557 | x = 2 + 558 |+ +558 559 | def b(): +559 560 | x = 1 +560 561 | def c(): + +E30.py:560:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +558 | def b(): +559 | x = 1 +560 | def c(): | ^^^ E306 -544 | pass -545 | # end +561 | pass +562 | # end | = help: Add missing blank line ℹ Safe fix -540 540 | x = 2 -541 541 | def b(): -542 542 | x = 1 - 543 |+ -543 544 | def c(): -544 545 | pass -545 546 | # end - -E30.py:551:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -549 | def a(): -550 | x = 1 -551 | class C: +557 557 | x = 2 +558 558 | def b(): +559 559 | x = 1 + 560 |+ +560 561 | def c(): +561 562 | pass +562 563 | # end + +E30.py:568:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +566 | def a(): +567 | x = 1 +568 | class C: | ^^^^^ E306 -552 | pass -553 | x = 2 +569 | pass +570 | x = 2 | = help: Add missing blank line ℹ Safe fix -548 548 | # E306:3:5 E306:6:5 -549 549 | def a(): -550 550 | x = 1 - 551 |+ -551 552 | class C: -552 553 | pass -553 554 | x = 2 - -E30.py:554:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -552 | pass -553 | x = 2 -554 | def b(): +565 565 | # E306:3:5 E306:6:5 +566 566 | def a(): +567 567 | x = 1 + 568 |+ +568 569 | class C: +569 570 | pass +570 571 | x = 2 + +E30.py:571:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +569 | pass +570 | x = 2 +571 | def b(): | ^^^ E306 -555 | pass -556 | # end +572 | pass +573 | # end | = help: Add missing blank line ℹ Safe fix -551 551 | class C: -552 552 | pass -553 553 | x = 2 - 554 |+ -554 555 | def b(): -555 556 | pass -556 557 | # end - -E30.py:563:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -561 | def bar(): -562 | pass -563 | def baz(): pass +568 568 | class C: +569 569 | pass +570 570 | x = 2 + 571 |+ +571 572 | def b(): +572 573 | pass +573 574 | # end + +E30.py:580:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +578 | def bar(): +579 | pass +580 | def baz(): pass | ^^^ E306 -564 | # end +581 | # end | = help: Add missing blank line ℹ Safe fix -560 560 | def foo(): -561 561 | def bar(): -562 562 | pass - 563 |+ -563 564 | def baz(): pass -564 565 | # end -565 566 | - -E30.py:570:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -568 | def foo(): -569 | def bar(): pass -570 | def baz(): +577 577 | def foo(): +578 578 | def bar(): +579 579 | pass + 580 |+ +580 581 | def baz(): pass +581 582 | # end +582 583 | + +E30.py:587:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +585 | def foo(): +586 | def bar(): pass +587 | def baz(): | ^^^ E306 -571 | pass -572 | # end +588 | pass +589 | # end | = help: Add missing blank line ℹ Safe fix -567 567 | # E306:3:5 -568 568 | def foo(): -569 569 | def bar(): pass - 570 |+ -570 571 | def baz(): -571 572 | pass -572 573 | # end +584 584 | # E306:3:5 +585 585 | def foo(): +586 586 | def bar(): pass + 587 |+ +587 588 | def baz(): +588 589 | pass +589 590 | # end From 3f6f207c26356918ec91d007c6f7097643d80223 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 17 Nov 2023 01:13:52 +0900 Subject: [PATCH 020/122] Fix decorator linked false positive. decorator -> comment -> decorator was causing a false positive. --- .../test/fixtures/pycodestyle/E30.py | 9 + .../rules/logical_lines/blank_lines.rs | 5 +- ...ules__pycodestyle__tests__E301_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E302_E30.py.snap | 224 ++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 150 ++++++------ ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++++----- ...ules__pycodestyle__tests__E306_E30.py.snap | 230 +++++++++--------- 8 files changed, 404 insertions(+), 392 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 38b54ac00ac6a..b8d47e814a62d 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -302,6 +302,15 @@ def function(self): # end +# no error +@decorator +# comment +@decorator +def function(): + pass +# end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index c614ed49262a9..b5121d7e6ca14 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -443,7 +443,7 @@ pub(crate) fn blank_lines( } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class | TokenKind::At) && !( // Allow decorators. - is_decorator(prev_line) + tracked_vars.follows_decorator // Allow groups of one-liners. || (tracked_vars.follows_def && line @@ -590,6 +590,9 @@ pub(crate) fn blank_lines( tracked_vars.follows_decorator = false; tracked_vars.follows_def = false; } + TokenKind::Comment => { + break; + } _ => { tracked_vars.follows_decorator = false; tracked_vars.follows_def = false; diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index 20ea2c959c2e3..d8c817189de21 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:310:5: E301 [*] Expected 1 blank line, found 0 +E30.py:319:5: E301 [*] Expected 1 blank line, found 0 | -308 | def func1(): -309 | pass -310 | def func2(): +317 | def func1(): +318 | pass +319 | def func2(): | ^^^ E301 -311 | pass -312 | # end +320 | pass +321 | # end | = help: Add missing blank line(s) ℹ Safe fix -307 307 | -308 308 | def func1(): -309 309 | pass - 310 |+ -310 311 | def func2(): -311 312 | pass -312 313 | # end +316 316 | +317 317 | def func1(): +318 318 | pass + 319 |+ +319 320 | def func2(): +320 321 | pass +321 322 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 4da9eb2f8b6d2..6734bee54b4ee 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,187 +1,187 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:328:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:337:1: E302 [*] Expected 2 blank lines, found 0 | -326 | # E302 -327 | """Main module.""" -328 | def fn(): +335 | # E302 +336 | """Main module.""" +337 | def fn(): | ^^^ E302 -329 | pass -330 | # end +338 | pass +339 | # end | = help: Add missing blank line(s) ℹ Safe fix -325 325 | -326 326 | # E302 -327 327 | """Main module.""" - 328 |+ - 329 |+ -328 330 | def fn(): -329 331 | pass -330 332 | # end - -E30.py:335:1: E302 [*] Expected 2 blank lines, found 0 - | -333 | # E302 -334 | import sys -335 | def get_sys_path(): +334 334 | +335 335 | # E302 +336 336 | """Main module.""" + 337 |+ + 338 |+ +337 339 | def fn(): +338 340 | pass +339 341 | # end + +E30.py:344:1: E302 [*] Expected 2 blank lines, found 0 + | +342 | # E302 +343 | import sys +344 | def get_sys_path(): | ^^^ E302 -336 | return sys.path -337 | # end - | - = help: Add missing blank line(s) - -ℹ Safe fix -332 332 | -333 333 | # E302 -334 334 | import sys - 335 |+ - 336 |+ -335 337 | def get_sys_path(): -336 338 | return sys.path -337 339 | # end - -E30.py:344:1: E302 [*] Expected 2 blank lines, found 1 - | -342 | pass -343 | -344 | def b(): - | ^^^ E302 -345 | pass +345 | return sys.path 346 | # end | = help: Add missing blank line(s) ℹ Safe fix -341 341 | def a(): -342 342 | pass -343 343 | +341 341 | +342 342 | # E302 +343 343 | import sys 344 |+ -344 345 | def b(): -345 346 | pass -346 347 | # end + 345 |+ +344 346 | def get_sys_path(): +345 347 | return sys.path +346 348 | # end -E30.py:355:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:353:1: E302 [*] Expected 2 blank lines, found 1 | -353 | # comment -354 | -355 | def b(): +351 | pass +352 | +353 | def b(): | ^^^ E302 -356 | pass -357 | # end +354 | pass +355 | # end | = help: Add missing blank line(s) ℹ Safe fix +350 350 | def a(): +351 351 | pass 352 352 | -353 353 | # comment -354 354 | - 355 |+ -355 356 | def b(): -356 357 | pass -357 358 | # end + 353 |+ +353 354 | def b(): +354 355 | pass +355 356 | # end -E30.py:364:7: E302 [*] Expected 2 blank lines, found 1 +E30.py:364:1: E302 [*] Expected 2 blank lines, found 1 | -362 | pass +362 | # comment 363 | -364 | async def b(): - | ^^^ E302 +364 | def b(): + | ^^^ E302 365 | pass 366 | # end | = help: Add missing blank line(s) ℹ Safe fix -361 361 | def a(): -362 362 | pass +361 361 | +362 362 | # comment 363 363 | 364 |+ -364 365 | async def b(): +364 365 | def b(): 365 366 | pass 366 367 | # end -E30.py:373:8: E302 [*] Expected 2 blank lines, found 1 +E30.py:373:7: E302 [*] Expected 2 blank lines, found 1 | 371 | pass 372 | -373 | async def x(y: int = 1): - | ^^^ E302 +373 | async def b(): + | ^^^ E302 374 | pass 375 | # end | = help: Add missing blank line(s) ℹ Safe fix -370 370 | async def x(): +370 370 | def a(): 371 371 | pass 372 372 | 373 |+ -373 374 | async def x(y: int = 1): +373 374 | async def b(): 374 375 | pass 375 376 | # end -E30.py:381:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:382:8: E302 [*] Expected 2 blank lines, found 1 | -379 | def bar(): 380 | pass -381 | def baz(): pass - | ^^^ E302 -382 | # end +381 | +382 | async def x(y: int = 1): + | ^^^ E302 +383 | pass +384 | # end | = help: Add missing blank line(s) ℹ Safe fix -378 378 | # E302 -379 379 | def bar(): +379 379 | async def x(): 380 380 | pass - 381 |+ +381 381 | 382 |+ -381 383 | def baz(): pass -382 384 | # end -383 385 | +382 383 | async def x(y: int = 1): +383 384 | pass +384 385 | # end -E30.py:387:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:390:1: E302 [*] Expected 2 blank lines, found 0 | -385 | # E302 -386 | def bar(): pass -387 | def baz(): +388 | def bar(): +389 | pass +390 | def baz(): pass | ^^^ E302 -388 | pass -389 | # end +391 | # end | = help: Add missing blank line(s) ℹ Safe fix -384 384 | -385 385 | # E302 -386 386 | def bar(): pass - 387 |+ - 388 |+ -387 389 | def baz(): -388 390 | pass -389 391 | # end - -E30.py:397:1: E302 [*] Expected 2 blank lines, found 0 - | -396 | # comment -397 | @decorator - | ^ E302 -398 | def g(): -399 | pass +387 387 | # E302 +388 388 | def bar(): +389 389 | pass + 390 |+ + 391 |+ +390 392 | def baz(): pass +391 393 | # end +392 394 | + +E30.py:396:1: E302 [*] Expected 2 blank lines, found 0 + | +394 | # E302 +395 | def bar(): pass +396 | def baz(): + | ^^^ E302 +397 | pass +398 | # end | = help: Add missing blank line(s) ℹ Safe fix -394 394 | pass -395 395 | -396 396 | # comment +393 393 | +394 394 | # E302 +395 395 | def bar(): pass + 396 |+ 397 |+ - 398 |+ -397 399 | @decorator -398 400 | def g(): -399 401 | pass +396 398 | def baz(): +397 399 | pass +398 400 | # end + +E30.py:406:1: E302 [*] Expected 2 blank lines, found 0 + | +405 | # comment +406 | @decorator + | ^ E302 +407 | def g(): +408 | pass + | + = help: Add missing blank line(s) + +ℹ Safe fix +403 403 | pass +404 404 | +405 405 | # comment + 406 |+ + 407 |+ +406 408 | @decorator +407 409 | def g(): +408 410 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 68f44ac931792..7c0d516629f63 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,128 +1,128 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:408:5: E303 [*] Too many blank lines (2) +E30.py:417:5: E303 [*] Too many blank lines (2) | -408 | # arbitrary comment +417 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -409 | -410 | def inner(): # E306 not expected +418 | +419 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -404 404 | def fn(): -405 405 | _ = None -406 406 | -407 |- -408 407 | # arbitrary comment -409 408 | -410 409 | def inner(): # E306 not expected +413 413 | def fn(): +414 414 | _ = None +415 415 | +416 |- +417 416 | # arbitrary comment +418 417 | +419 418 | def inner(): # E306 not expected -E30.py:420:5: E303 [*] Too many blank lines (2) +E30.py:429:5: E303 [*] Too many blank lines (2) | -420 | # arbitrary comment +429 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -421 | def inner(): # E306 not expected -422 | pass +430 | def inner(): # E306 not expected +431 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -416 416 | def fn(): -417 417 | _ = None -418 418 | -419 |- -420 419 | # arbitrary comment -421 420 | def inner(): # E306 not expected -422 421 | pass +425 425 | def fn(): +426 426 | _ = None +427 427 | +428 |- +429 428 | # arbitrary comment +430 429 | def inner(): # E306 not expected +431 430 | pass -E30.py:431:1: E303 [*] Too many blank lines (3) +E30.py:440:1: E303 [*] Too many blank lines (3) | -431 | print() +440 | print() | ^^^^^ E303 -432 | # end +441 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -427 427 | print() -428 428 | -429 429 | -430 |- -431 430 | print() -432 431 | # end -433 432 | +436 436 | print() +437 437 | +438 438 | +439 |- +440 439 | print() +441 440 | # end +442 441 | -E30.py:440:1: E303 [*] Too many blank lines (3) +E30.py:449:1: E303 [*] Too many blank lines (3) | -440 | # comment +449 | # comment | ^^^^^^^^^ E303 -441 | -442 | print() +450 | +451 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -436 436 | print() -437 437 | -438 438 | -439 |- -440 439 | # comment -441 440 | -442 441 | print() +445 445 | print() +446 446 | +447 447 | +448 |- +449 448 | # comment +450 449 | +451 450 | print() -E30.py:451:5: E303 [*] Too many blank lines (2) +E30.py:460:5: E303 [*] Too many blank lines (2) | -451 | # comment +460 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -447 447 | def a(): -448 448 | print() -449 449 | -450 |- -451 450 | # comment -452 451 | -453 452 | +456 456 | def a(): +457 457 | print() +458 458 | +459 |- +460 459 | # comment +461 460 | +462 461 | -E30.py:454:5: E303 [*] Too many blank lines (2) +E30.py:463:5: E303 [*] Too many blank lines (2) | -454 | # another comment +463 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -455 | -456 | print() +464 | +465 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -450 450 | -451 451 | # comment -452 452 | -453 |- -454 453 | # another comment -455 454 | -456 455 | print() +459 459 | +460 460 | # comment +461 461 | +462 |- +463 462 | # another comment +464 463 | +465 464 | print() -E30.py:465:1: E303 [*] Too many blank lines (3) +E30.py:474:1: E303 [*] Too many blank lines (3) | -465 | / """This class docstring comes on line 5. -466 | | It gives error E303: too many blank lines (3) -467 | | """ +474 | / """This class docstring comes on line 5. +475 | | It gives error E303: too many blank lines (3) +476 | | """ | |___^ E303 -468 | # end +477 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -461 461 | #!python -462 462 | -463 463 | -464 |- -465 464 | """This class docstring comes on line 5. -466 465 | It gives error E303: too many blank lines (3) -467 466 | """ +470 470 | #!python +471 471 | +472 472 | +473 |- +474 473 | """This class docstring comes on line 5. +475 474 | It gives error E303: too many blank lines (3) +476 475 | """ diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index f8dd02af6ad24..88f0093a0a61f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:474:1: E304 [*] blank lines found after function decorator +E30.py:483:1: E304 [*] blank lines found after function decorator | -472 | @decorator -473 | -474 | def function(): +481 | @decorator +482 | +483 | def function(): | ^^^ E304 -475 | pass -476 | # end +484 | pass +485 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -470 470 | -471 471 | # E304 -472 472 | @decorator -473 |- -474 473 | def function(): -475 474 | pass -476 475 | # end +479 479 | +480 480 | # E304 +481 481 | @decorator +482 |- +483 482 | def function(): +484 483 | pass +485 484 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 695ecbdc524c3..b272a4808ab1b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:486:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:495:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -485 | # another comment -486 | fn() +494 | # another comment +495 | fn() | ^^ E305 -487 | # end +496 | # end | = help: Add missing blank line(s) ℹ Safe fix -483 483 | # comment -484 484 | -485 485 | # another comment - 486 |+ - 487 |+ -486 488 | fn() -487 489 | # end -488 490 | +492 492 | # comment +493 493 | +494 494 | # another comment + 495 |+ + 496 |+ +495 497 | fn() +496 498 | # end +497 499 | -E30.py:497:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:506:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -496 | # another comment -497 | a = 1 +505 | # another comment +506 | a = 1 | ^ E305 -498 | # end +507 | # end | = help: Add missing blank line(s) ℹ Safe fix -494 494 | # comment -495 495 | -496 496 | # another comment - 497 |+ - 498 |+ -497 499 | a = 1 -498 500 | # end -499 501 | +503 503 | # comment +504 504 | +505 505 | # another comment + 506 |+ + 507 |+ +506 508 | a = 1 +507 509 | # end +508 510 | -E30.py:509:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:518:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -507 | # another comment -508 | -509 | try: +516 | # another comment +517 | +518 | try: | ^^^ E305 -510 | fn() -511 | except Exception: +519 | fn() +520 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -506 506 | -507 507 | # another comment -508 508 | - 509 |+ -509 510 | try: -510 511 | fn() -511 512 | except Exception: +515 515 | +516 516 | # another comment +517 517 | + 518 |+ +518 519 | try: +519 520 | fn() +520 521 | except Exception: -E30.py:521:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:530:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -520 | # Two spaces before comments, too. -521 | if a(): +529 | # Two spaces before comments, too. +530 | if a(): | ^^ E305 -522 | a() -523 | # end +531 | a() +532 | # end | = help: Add missing blank line(s) ℹ Safe fix -518 518 | print -519 519 | -520 520 | # Two spaces before comments, too. - 521 |+ - 522 |+ -521 523 | if a(): -522 524 | a() -523 525 | # end +527 527 | print +528 528 | +529 529 | # Two spaces before comments, too. + 530 |+ + 531 |+ +530 532 | if a(): +531 533 | a() +532 534 | # end -E30.py:534:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:543:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -532 | blah, blah -533 | -534 | if __name__ == '__main__': +541 | blah, blah +542 | +543 | if __name__ == '__main__': | ^^ E305 -535 | main() -536 | # end +544 | main() +545 | # end | = help: Add missing blank line(s) ℹ Safe fix -531 531 | def main(): -532 532 | blah, blah -533 533 | - 534 |+ -534 535 | if __name__ == '__main__': -535 536 | main() -536 537 | # end +540 540 | def main(): +541 541 | blah, blah +542 542 | + 543 |+ +543 544 | if __name__ == '__main__': +544 545 | main() +545 546 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 4029c27c5056f..393e7df4b4175 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:542:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:551:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -540 | def a(): -541 | x = 1 -542 | def b(): +549 | def a(): +550 | x = 1 +551 | def b(): | ^^^ E306 -543 | pass -544 | # end +552 | pass +553 | # end | = help: Add missing blank line ℹ Safe fix -539 539 | # E306:3:5 -540 540 | def a(): -541 541 | x = 1 - 542 |+ -542 543 | def b(): -543 544 | pass -544 545 | # end - -E30.py:550:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -548 | async def a(): -549 | x = 1 -550 | def b(): +548 548 | # E306:3:5 +549 549 | def a(): +550 550 | x = 1 + 551 |+ +551 552 | def b(): +552 553 | pass +553 554 | # end + +E30.py:559:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +557 | async def a(): +558 | x = 1 +559 | def b(): | ^^^ E306 -551 | pass -552 | # end +560 | pass +561 | # end | = help: Add missing blank line ℹ Safe fix -547 547 | #: E306:3:5 -548 548 | async def a(): -549 549 | x = 1 - 550 |+ -550 551 | def b(): -551 552 | pass -552 553 | # end - -E30.py:558:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -556 | def a(): -557 | x = 2 -558 | def b(): +556 556 | #: E306:3:5 +557 557 | async def a(): +558 558 | x = 1 + 559 |+ +559 560 | def b(): +560 561 | pass +561 562 | # end + +E30.py:567:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +565 | def a(): +566 | x = 2 +567 | def b(): | ^^^ E306 -559 | x = 1 -560 | def c(): +568 | x = 1 +569 | def c(): | = help: Add missing blank line ℹ Safe fix -555 555 | #: E306:3:5 E306:5:9 -556 556 | def a(): -557 557 | x = 2 - 558 |+ -558 559 | def b(): -559 560 | x = 1 -560 561 | def c(): - -E30.py:560:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -558 | def b(): -559 | x = 1 -560 | def c(): +564 564 | #: E306:3:5 E306:5:9 +565 565 | def a(): +566 566 | x = 2 + 567 |+ +567 568 | def b(): +568 569 | x = 1 +569 570 | def c(): + +E30.py:569:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +567 | def b(): +568 | x = 1 +569 | def c(): | ^^^ E306 -561 | pass -562 | # end +570 | pass +571 | # end | = help: Add missing blank line ℹ Safe fix -557 557 | x = 2 -558 558 | def b(): -559 559 | x = 1 - 560 |+ -560 561 | def c(): -561 562 | pass -562 563 | # end - -E30.py:568:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -566 | def a(): -567 | x = 1 -568 | class C: +566 566 | x = 2 +567 567 | def b(): +568 568 | x = 1 + 569 |+ +569 570 | def c(): +570 571 | pass +571 572 | # end + +E30.py:577:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +575 | def a(): +576 | x = 1 +577 | class C: | ^^^^^ E306 -569 | pass -570 | x = 2 +578 | pass +579 | x = 2 | = help: Add missing blank line ℹ Safe fix -565 565 | # E306:3:5 E306:6:5 -566 566 | def a(): -567 567 | x = 1 - 568 |+ -568 569 | class C: -569 570 | pass -570 571 | x = 2 - -E30.py:571:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -569 | pass -570 | x = 2 -571 | def b(): +574 574 | # E306:3:5 E306:6:5 +575 575 | def a(): +576 576 | x = 1 + 577 |+ +577 578 | class C: +578 579 | pass +579 580 | x = 2 + +E30.py:580:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +578 | pass +579 | x = 2 +580 | def b(): | ^^^ E306 -572 | pass -573 | # end +581 | pass +582 | # end | = help: Add missing blank line ℹ Safe fix -568 568 | class C: -569 569 | pass -570 570 | x = 2 - 571 |+ -571 572 | def b(): -572 573 | pass -573 574 | # end +577 577 | class C: +578 578 | pass +579 579 | x = 2 + 580 |+ +580 581 | def b(): +581 582 | pass +582 583 | # end -E30.py:580:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:589:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -578 | def bar(): -579 | pass -580 | def baz(): pass +587 | def bar(): +588 | pass +589 | def baz(): pass | ^^^ E306 -581 | # end +590 | # end | = help: Add missing blank line ℹ Safe fix -577 577 | def foo(): -578 578 | def bar(): -579 579 | pass - 580 |+ -580 581 | def baz(): pass -581 582 | # end -582 583 | - -E30.py:587:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -585 | def foo(): -586 | def bar(): pass -587 | def baz(): +586 586 | def foo(): +587 587 | def bar(): +588 588 | pass + 589 |+ +589 590 | def baz(): pass +590 591 | # end +591 592 | + +E30.py:596:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +594 | def foo(): +595 | def bar(): pass +596 | def baz(): | ^^^ E306 -588 | pass -589 | # end +597 | pass +598 | # end | = help: Add missing blank line ℹ Safe fix -584 584 | # E306:3:5 -585 585 | def foo(): -586 586 | def bar(): pass - 587 |+ -587 588 | def baz(): -588 589 | pass -589 590 | # end +593 593 | # E306:3:5 +594 594 | def foo(): +595 595 | def bar(): pass + 596 |+ +596 597 | def baz(): +597 598 | pass +598 599 | # end From b667c5cb887e28e40f3f378cd749562d9fc9f112 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 17 Nov 2023 11:11:59 +0900 Subject: [PATCH 021/122] Fix E301 false positive. Fix false positive where a method following an if (or other indentation inducing keyword) would trigger E301. --- .../test/fixtures/pycodestyle/E30.py | 9 + .../rules/logical_lines/blank_lines.rs | 4 +- ...ules__pycodestyle__tests__E301_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E302_E30.py.snap | 224 ++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 150 ++++++------ ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++++----- ...ules__pycodestyle__tests__E306_E30.py.snap | 230 +++++++++--------- 8 files changed, 403 insertions(+), 392 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index b8d47e814a62d..2012b42a8c0d2 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -311,6 +311,15 @@ def function(): # end +# no error +class Class: + def method(self): + if True: + def function(): + pass +# end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index b5121d7e6ca14..6a0293005f35a 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -415,6 +415,8 @@ pub(crate) fn blank_lines( && tracked_vars.is_in_class // The class's docstring can directly precede the first function. && !is_docstring(prev_line) + // Do not trigger when then def follows an if/while/etc... + && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) && ( // A comment before the def is allowed (as long as it is preceded by a blank line). (line.line.preceding_blank_lines == 0 && line.line.blank_lines == 0 && prev_line.is_some_and(LogicalLine::is_comment_only)) @@ -425,7 +427,7 @@ pub(crate) fn blank_lines( .map_or(false, |token| { !matches!( token.kind(), - TokenKind::Def | TokenKind::Class | TokenKind::At + TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async ) }) ) diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index d8c817189de21..5fe3dc3314ae8 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:319:5: E301 [*] Expected 1 blank line, found 0 +E30.py:328:5: E301 [*] Expected 1 blank line, found 0 | -317 | def func1(): -318 | pass -319 | def func2(): +326 | def func1(): +327 | pass +328 | def func2(): | ^^^ E301 -320 | pass -321 | # end +329 | pass +330 | # end | = help: Add missing blank line(s) ℹ Safe fix -316 316 | -317 317 | def func1(): -318 318 | pass - 319 |+ -319 320 | def func2(): -320 321 | pass -321 322 | # end +325 325 | +326 326 | def func1(): +327 327 | pass + 328 |+ +328 329 | def func2(): +329 330 | pass +330 331 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 6734bee54b4ee..f848854dad843 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,187 +1,187 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:337:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:346:1: E302 [*] Expected 2 blank lines, found 0 | -335 | # E302 -336 | """Main module.""" -337 | def fn(): +344 | # E302 +345 | """Main module.""" +346 | def fn(): | ^^^ E302 -338 | pass -339 | # end +347 | pass +348 | # end | = help: Add missing blank line(s) ℹ Safe fix -334 334 | -335 335 | # E302 -336 336 | """Main module.""" - 337 |+ - 338 |+ -337 339 | def fn(): -338 340 | pass -339 341 | # end - -E30.py:344:1: E302 [*] Expected 2 blank lines, found 0 - | -342 | # E302 -343 | import sys -344 | def get_sys_path(): +343 343 | +344 344 | # E302 +345 345 | """Main module.""" + 346 |+ + 347 |+ +346 348 | def fn(): +347 349 | pass +348 350 | # end + +E30.py:353:1: E302 [*] Expected 2 blank lines, found 0 + | +351 | # E302 +352 | import sys +353 | def get_sys_path(): | ^^^ E302 -345 | return sys.path -346 | # end - | - = help: Add missing blank line(s) - -ℹ Safe fix -341 341 | -342 342 | # E302 -343 343 | import sys - 344 |+ - 345 |+ -344 346 | def get_sys_path(): -345 347 | return sys.path -346 348 | # end - -E30.py:353:1: E302 [*] Expected 2 blank lines, found 1 - | -351 | pass -352 | -353 | def b(): - | ^^^ E302 -354 | pass +354 | return sys.path 355 | # end | = help: Add missing blank line(s) ℹ Safe fix -350 350 | def a(): -351 351 | pass -352 352 | +350 350 | +351 351 | # E302 +352 352 | import sys 353 |+ -353 354 | def b(): -354 355 | pass -355 356 | # end + 354 |+ +353 355 | def get_sys_path(): +354 356 | return sys.path +355 357 | # end -E30.py:364:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:362:1: E302 [*] Expected 2 blank lines, found 1 | -362 | # comment -363 | -364 | def b(): +360 | pass +361 | +362 | def b(): | ^^^ E302 -365 | pass -366 | # end +363 | pass +364 | # end | = help: Add missing blank line(s) ℹ Safe fix +359 359 | def a(): +360 360 | pass 361 361 | -362 362 | # comment -363 363 | - 364 |+ -364 365 | def b(): -365 366 | pass -366 367 | # end + 362 |+ +362 363 | def b(): +363 364 | pass +364 365 | # end -E30.py:373:7: E302 [*] Expected 2 blank lines, found 1 +E30.py:373:1: E302 [*] Expected 2 blank lines, found 1 | -371 | pass +371 | # comment 372 | -373 | async def b(): - | ^^^ E302 +373 | def b(): + | ^^^ E302 374 | pass 375 | # end | = help: Add missing blank line(s) ℹ Safe fix -370 370 | def a(): -371 371 | pass +370 370 | +371 371 | # comment 372 372 | 373 |+ -373 374 | async def b(): +373 374 | def b(): 374 375 | pass 375 376 | # end -E30.py:382:8: E302 [*] Expected 2 blank lines, found 1 +E30.py:382:7: E302 [*] Expected 2 blank lines, found 1 | 380 | pass 381 | -382 | async def x(y: int = 1): - | ^^^ E302 +382 | async def b(): + | ^^^ E302 383 | pass 384 | # end | = help: Add missing blank line(s) ℹ Safe fix -379 379 | async def x(): +379 379 | def a(): 380 380 | pass 381 381 | 382 |+ -382 383 | async def x(y: int = 1): +382 383 | async def b(): 383 384 | pass 384 385 | # end -E30.py:390:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:391:8: E302 [*] Expected 2 blank lines, found 1 | -388 | def bar(): 389 | pass -390 | def baz(): pass - | ^^^ E302 -391 | # end +390 | +391 | async def x(y: int = 1): + | ^^^ E302 +392 | pass +393 | # end | = help: Add missing blank line(s) ℹ Safe fix -387 387 | # E302 -388 388 | def bar(): +388 388 | async def x(): 389 389 | pass - 390 |+ +390 390 | 391 |+ -390 392 | def baz(): pass -391 393 | # end -392 394 | +391 392 | async def x(y: int = 1): +392 393 | pass +393 394 | # end -E30.py:396:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:399:1: E302 [*] Expected 2 blank lines, found 0 | -394 | # E302 -395 | def bar(): pass -396 | def baz(): +397 | def bar(): +398 | pass +399 | def baz(): pass | ^^^ E302 -397 | pass -398 | # end +400 | # end | = help: Add missing blank line(s) ℹ Safe fix -393 393 | -394 394 | # E302 -395 395 | def bar(): pass - 396 |+ - 397 |+ -396 398 | def baz(): -397 399 | pass -398 400 | # end - -E30.py:406:1: E302 [*] Expected 2 blank lines, found 0 - | -405 | # comment -406 | @decorator - | ^ E302 -407 | def g(): -408 | pass +396 396 | # E302 +397 397 | def bar(): +398 398 | pass + 399 |+ + 400 |+ +399 401 | def baz(): pass +400 402 | # end +401 403 | + +E30.py:405:1: E302 [*] Expected 2 blank lines, found 0 + | +403 | # E302 +404 | def bar(): pass +405 | def baz(): + | ^^^ E302 +406 | pass +407 | # end | = help: Add missing blank line(s) ℹ Safe fix -403 403 | pass -404 404 | -405 405 | # comment +402 402 | +403 403 | # E302 +404 404 | def bar(): pass + 405 |+ 406 |+ - 407 |+ -406 408 | @decorator -407 409 | def g(): -408 410 | pass +405 407 | def baz(): +406 408 | pass +407 409 | # end + +E30.py:415:1: E302 [*] Expected 2 blank lines, found 0 + | +414 | # comment +415 | @decorator + | ^ E302 +416 | def g(): +417 | pass + | + = help: Add missing blank line(s) + +ℹ Safe fix +412 412 | pass +413 413 | +414 414 | # comment + 415 |+ + 416 |+ +415 417 | @decorator +416 418 | def g(): +417 419 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 7c0d516629f63..1f33ed52b7584 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,128 +1,128 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:417:5: E303 [*] Too many blank lines (2) +E30.py:426:5: E303 [*] Too many blank lines (2) | -417 | # arbitrary comment +426 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -418 | -419 | def inner(): # E306 not expected +427 | +428 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -413 413 | def fn(): -414 414 | _ = None -415 415 | -416 |- -417 416 | # arbitrary comment -418 417 | -419 418 | def inner(): # E306 not expected +422 422 | def fn(): +423 423 | _ = None +424 424 | +425 |- +426 425 | # arbitrary comment +427 426 | +428 427 | def inner(): # E306 not expected -E30.py:429:5: E303 [*] Too many blank lines (2) +E30.py:438:5: E303 [*] Too many blank lines (2) | -429 | # arbitrary comment +438 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -430 | def inner(): # E306 not expected -431 | pass +439 | def inner(): # E306 not expected +440 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -425 425 | def fn(): -426 426 | _ = None -427 427 | -428 |- -429 428 | # arbitrary comment -430 429 | def inner(): # E306 not expected -431 430 | pass +434 434 | def fn(): +435 435 | _ = None +436 436 | +437 |- +438 437 | # arbitrary comment +439 438 | def inner(): # E306 not expected +440 439 | pass -E30.py:440:1: E303 [*] Too many blank lines (3) +E30.py:449:1: E303 [*] Too many blank lines (3) | -440 | print() +449 | print() | ^^^^^ E303 -441 | # end +450 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -436 436 | print() -437 437 | -438 438 | -439 |- -440 439 | print() -441 440 | # end -442 441 | +445 445 | print() +446 446 | +447 447 | +448 |- +449 448 | print() +450 449 | # end +451 450 | -E30.py:449:1: E303 [*] Too many blank lines (3) +E30.py:458:1: E303 [*] Too many blank lines (3) | -449 | # comment +458 | # comment | ^^^^^^^^^ E303 -450 | -451 | print() +459 | +460 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -445 445 | print() -446 446 | -447 447 | -448 |- -449 448 | # comment -450 449 | -451 450 | print() +454 454 | print() +455 455 | +456 456 | +457 |- +458 457 | # comment +459 458 | +460 459 | print() -E30.py:460:5: E303 [*] Too many blank lines (2) +E30.py:469:5: E303 [*] Too many blank lines (2) | -460 | # comment +469 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -456 456 | def a(): -457 457 | print() -458 458 | -459 |- -460 459 | # comment -461 460 | -462 461 | +465 465 | def a(): +466 466 | print() +467 467 | +468 |- +469 468 | # comment +470 469 | +471 470 | -E30.py:463:5: E303 [*] Too many blank lines (2) +E30.py:472:5: E303 [*] Too many blank lines (2) | -463 | # another comment +472 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -464 | -465 | print() +473 | +474 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -459 459 | -460 460 | # comment -461 461 | -462 |- -463 462 | # another comment -464 463 | -465 464 | print() +468 468 | +469 469 | # comment +470 470 | +471 |- +472 471 | # another comment +473 472 | +474 473 | print() -E30.py:474:1: E303 [*] Too many blank lines (3) +E30.py:483:1: E303 [*] Too many blank lines (3) | -474 | / """This class docstring comes on line 5. -475 | | It gives error E303: too many blank lines (3) -476 | | """ +483 | / """This class docstring comes on line 5. +484 | | It gives error E303: too many blank lines (3) +485 | | """ | |___^ E303 -477 | # end +486 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -470 470 | #!python -471 471 | -472 472 | -473 |- -474 473 | """This class docstring comes on line 5. -475 474 | It gives error E303: too many blank lines (3) -476 475 | """ +479 479 | #!python +480 480 | +481 481 | +482 |- +483 482 | """This class docstring comes on line 5. +484 483 | It gives error E303: too many blank lines (3) +485 484 | """ diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 88f0093a0a61f..e5d7273923fd4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:483:1: E304 [*] blank lines found after function decorator +E30.py:492:1: E304 [*] blank lines found after function decorator | -481 | @decorator -482 | -483 | def function(): +490 | @decorator +491 | +492 | def function(): | ^^^ E304 -484 | pass -485 | # end +493 | pass +494 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -479 479 | -480 480 | # E304 -481 481 | @decorator -482 |- -483 482 | def function(): -484 483 | pass -485 484 | # end +488 488 | +489 489 | # E304 +490 490 | @decorator +491 |- +492 491 | def function(): +493 492 | pass +494 493 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index b272a4808ab1b..e47361ed27a32 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:495:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:504:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -494 | # another comment -495 | fn() +503 | # another comment +504 | fn() | ^^ E305 -496 | # end +505 | # end | = help: Add missing blank line(s) ℹ Safe fix -492 492 | # comment -493 493 | -494 494 | # another comment - 495 |+ - 496 |+ -495 497 | fn() -496 498 | # end -497 499 | +501 501 | # comment +502 502 | +503 503 | # another comment + 504 |+ + 505 |+ +504 506 | fn() +505 507 | # end +506 508 | -E30.py:506:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:515:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -505 | # another comment -506 | a = 1 +514 | # another comment +515 | a = 1 | ^ E305 -507 | # end +516 | # end | = help: Add missing blank line(s) ℹ Safe fix -503 503 | # comment -504 504 | -505 505 | # another comment - 506 |+ - 507 |+ -506 508 | a = 1 -507 509 | # end -508 510 | +512 512 | # comment +513 513 | +514 514 | # another comment + 515 |+ + 516 |+ +515 517 | a = 1 +516 518 | # end +517 519 | -E30.py:518:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:527:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -516 | # another comment -517 | -518 | try: +525 | # another comment +526 | +527 | try: | ^^^ E305 -519 | fn() -520 | except Exception: +528 | fn() +529 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -515 515 | -516 516 | # another comment -517 517 | - 518 |+ -518 519 | try: -519 520 | fn() -520 521 | except Exception: +524 524 | +525 525 | # another comment +526 526 | + 527 |+ +527 528 | try: +528 529 | fn() +529 530 | except Exception: -E30.py:530:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:539:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -529 | # Two spaces before comments, too. -530 | if a(): +538 | # Two spaces before comments, too. +539 | if a(): | ^^ E305 -531 | a() -532 | # end +540 | a() +541 | # end | = help: Add missing blank line(s) ℹ Safe fix -527 527 | print -528 528 | -529 529 | # Two spaces before comments, too. - 530 |+ - 531 |+ -530 532 | if a(): -531 533 | a() -532 534 | # end +536 536 | print +537 537 | +538 538 | # Two spaces before comments, too. + 539 |+ + 540 |+ +539 541 | if a(): +540 542 | a() +541 543 | # end -E30.py:543:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:552:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -541 | blah, blah -542 | -543 | if __name__ == '__main__': +550 | blah, blah +551 | +552 | if __name__ == '__main__': | ^^ E305 -544 | main() -545 | # end +553 | main() +554 | # end | = help: Add missing blank line(s) ℹ Safe fix -540 540 | def main(): -541 541 | blah, blah -542 542 | - 543 |+ -543 544 | if __name__ == '__main__': -544 545 | main() -545 546 | # end +549 549 | def main(): +550 550 | blah, blah +551 551 | + 552 |+ +552 553 | if __name__ == '__main__': +553 554 | main() +554 555 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 393e7df4b4175..a5184464f9869 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:551:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:560:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -549 | def a(): -550 | x = 1 -551 | def b(): +558 | def a(): +559 | x = 1 +560 | def b(): | ^^^ E306 -552 | pass -553 | # end +561 | pass +562 | # end | = help: Add missing blank line ℹ Safe fix -548 548 | # E306:3:5 -549 549 | def a(): -550 550 | x = 1 - 551 |+ -551 552 | def b(): -552 553 | pass -553 554 | # end - -E30.py:559:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -557 | async def a(): -558 | x = 1 -559 | def b(): +557 557 | # E306:3:5 +558 558 | def a(): +559 559 | x = 1 + 560 |+ +560 561 | def b(): +561 562 | pass +562 563 | # end + +E30.py:568:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +566 | async def a(): +567 | x = 1 +568 | def b(): | ^^^ E306 -560 | pass -561 | # end +569 | pass +570 | # end | = help: Add missing blank line ℹ Safe fix -556 556 | #: E306:3:5 -557 557 | async def a(): -558 558 | x = 1 - 559 |+ -559 560 | def b(): -560 561 | pass -561 562 | # end - -E30.py:567:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -565 | def a(): -566 | x = 2 -567 | def b(): +565 565 | #: E306:3:5 +566 566 | async def a(): +567 567 | x = 1 + 568 |+ +568 569 | def b(): +569 570 | pass +570 571 | # end + +E30.py:576:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +574 | def a(): +575 | x = 2 +576 | def b(): | ^^^ E306 -568 | x = 1 -569 | def c(): +577 | x = 1 +578 | def c(): | = help: Add missing blank line ℹ Safe fix -564 564 | #: E306:3:5 E306:5:9 -565 565 | def a(): -566 566 | x = 2 - 567 |+ -567 568 | def b(): -568 569 | x = 1 -569 570 | def c(): - -E30.py:569:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -567 | def b(): -568 | x = 1 -569 | def c(): +573 573 | #: E306:3:5 E306:5:9 +574 574 | def a(): +575 575 | x = 2 + 576 |+ +576 577 | def b(): +577 578 | x = 1 +578 579 | def c(): + +E30.py:578:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +576 | def b(): +577 | x = 1 +578 | def c(): | ^^^ E306 -570 | pass -571 | # end +579 | pass +580 | # end | = help: Add missing blank line ℹ Safe fix -566 566 | x = 2 -567 567 | def b(): -568 568 | x = 1 - 569 |+ -569 570 | def c(): -570 571 | pass -571 572 | # end - -E30.py:577:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -575 | def a(): -576 | x = 1 -577 | class C: +575 575 | x = 2 +576 576 | def b(): +577 577 | x = 1 + 578 |+ +578 579 | def c(): +579 580 | pass +580 581 | # end + +E30.py:586:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +584 | def a(): +585 | x = 1 +586 | class C: | ^^^^^ E306 -578 | pass -579 | x = 2 +587 | pass +588 | x = 2 | = help: Add missing blank line ℹ Safe fix -574 574 | # E306:3:5 E306:6:5 -575 575 | def a(): -576 576 | x = 1 - 577 |+ -577 578 | class C: -578 579 | pass -579 580 | x = 2 - -E30.py:580:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -578 | pass -579 | x = 2 -580 | def b(): +583 583 | # E306:3:5 E306:6:5 +584 584 | def a(): +585 585 | x = 1 + 586 |+ +586 587 | class C: +587 588 | pass +588 589 | x = 2 + +E30.py:589:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +587 | pass +588 | x = 2 +589 | def b(): | ^^^ E306 -581 | pass -582 | # end +590 | pass +591 | # end | = help: Add missing blank line ℹ Safe fix -577 577 | class C: -578 578 | pass -579 579 | x = 2 - 580 |+ -580 581 | def b(): -581 582 | pass -582 583 | # end +586 586 | class C: +587 587 | pass +588 588 | x = 2 + 589 |+ +589 590 | def b(): +590 591 | pass +591 592 | # end -E30.py:589:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:598:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -587 | def bar(): -588 | pass -589 | def baz(): pass +596 | def bar(): +597 | pass +598 | def baz(): pass | ^^^ E306 -590 | # end +599 | # end | = help: Add missing blank line ℹ Safe fix -586 586 | def foo(): -587 587 | def bar(): -588 588 | pass - 589 |+ -589 590 | def baz(): pass -590 591 | # end -591 592 | - -E30.py:596:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -594 | def foo(): -595 | def bar(): pass -596 | def baz(): +595 595 | def foo(): +596 596 | def bar(): +597 597 | pass + 598 |+ +598 599 | def baz(): pass +599 600 | # end +600 601 | + +E30.py:605:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +603 | def foo(): +604 | def bar(): pass +605 | def baz(): | ^^^ E306 -597 | pass -598 | # end +606 | pass +607 | # end | = help: Add missing blank line ℹ Safe fix -593 593 | # E306:3:5 -594 594 | def foo(): -595 595 | def bar(): pass - 596 |+ -596 597 | def baz(): -597 598 | pass -598 599 | # end +602 602 | # E306:3:5 +603 603 | def foo(): +604 604 | def bar(): pass + 605 |+ +605 606 | def baz(): +606 607 | pass +607 608 | # end From 0bf4c45ea70690568e738eb974a44dc518e27ba6 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 18 Nov 2023 17:52:27 +0900 Subject: [PATCH 022/122] Fix decorator + async false positive. This also move the trigger on an async def from the def to the async. --- .../test/fixtures/pycodestyle/E30.py | 7 + .../rules/logical_lines/blank_lines.rs | 8 +- ...ules__pycodestyle__tests__E301_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E302_E30.py.snap | 254 +++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 150 +++++------ ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 ++++----- ...ules__pycodestyle__tests__E306_E30.py.snap | 226 ++++++++-------- 8 files changed, 413 insertions(+), 410 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 2012b42a8c0d2..f65760b609f95 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -320,6 +320,13 @@ def function(): # end +# no error +@decorator +async def function(data: None) -> None: + ... +# end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 6a0293005f35a..91f90cd1abecc 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -442,7 +442,7 @@ pub(crate) fn blank_lines( locator.line_start(token.range.start()), ))); context.push_diagnostic(diagnostic); - } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class | TokenKind::At) + } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async) && !( // Allow decorators. tracked_vars.follows_decorator @@ -579,7 +579,7 @@ pub(crate) fn blank_lines( tracked_vars.follows_def = false; break; } - TokenKind::Def => { + TokenKind::Def | TokenKind::Async => { if !tracked_vars.is_in_fn { tracked_vars.fn_indent_level = indent_level + indent_size; } @@ -588,10 +588,6 @@ pub(crate) fn blank_lines( tracked_vars.follows_decorator = false; break; } - TokenKind::Async => { - tracked_vars.follows_decorator = false; - tracked_vars.follows_def = false; - } TokenKind::Comment => { break; } diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index 5fe3dc3314ae8..b0fb810596f30 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:328:5: E301 [*] Expected 1 blank line, found 0 +E30.py:335:5: E301 [*] Expected 1 blank line, found 0 | -326 | def func1(): -327 | pass -328 | def func2(): +333 | def func1(): +334 | pass +335 | def func2(): | ^^^ E301 -329 | pass -330 | # end +336 | pass +337 | # end | = help: Add missing blank line(s) ℹ Safe fix -325 325 | -326 326 | def func1(): -327 327 | pass - 328 |+ -328 329 | def func2(): -329 330 | pass -330 331 | # end +332 332 | +333 333 | def func1(): +334 334 | pass + 335 |+ +335 336 | def func2(): +336 337 | pass +337 338 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index f848854dad843..a5a9a83bd17ed 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,34 +1,13 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:346:1: E302 [*] Expected 2 blank lines, found 0 - | -344 | # E302 -345 | """Main module.""" -346 | def fn(): - | ^^^ E302 -347 | pass -348 | # end - | - = help: Add missing blank line(s) - -ℹ Safe fix -343 343 | -344 344 | # E302 -345 345 | """Main module.""" - 346 |+ - 347 |+ -346 348 | def fn(): -347 349 | pass -348 350 | # end - E30.py:353:1: E302 [*] Expected 2 blank lines, found 0 | 351 | # E302 -352 | import sys -353 | def get_sys_path(): +352 | """Main module.""" +353 | def fn(): | ^^^ E302 -354 | return sys.path +354 | pass 355 | # end | = help: Add missing blank line(s) @@ -36,152 +15,173 @@ E30.py:353:1: E302 [*] Expected 2 blank lines, found 0 ℹ Safe fix 350 350 | 351 351 | # E302 -352 352 | import sys +352 352 | """Main module.""" 353 |+ 354 |+ -353 355 | def get_sys_path(): -354 356 | return sys.path +353 355 | def fn(): +354 356 | pass 355 357 | # end -E30.py:362:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:360:1: E302 [*] Expected 2 blank lines, found 0 | -360 | pass -361 | -362 | def b(): +358 | # E302 +359 | import sys +360 | def get_sys_path(): | ^^^ E302 -363 | pass -364 | # end +361 | return sys.path +362 | # end | = help: Add missing blank line(s) ℹ Safe fix -359 359 | def a(): -360 360 | pass -361 361 | - 362 |+ -362 363 | def b(): -363 364 | pass -364 365 | # end - -E30.py:373:1: E302 [*] Expected 2 blank lines, found 1 - | -371 | # comment -372 | -373 | def b(): +357 357 | +358 358 | # E302 +359 359 | import sys + 360 |+ + 361 |+ +360 362 | def get_sys_path(): +361 363 | return sys.path +362 364 | # end + +E30.py:369:1: E302 [*] Expected 2 blank lines, found 1 + | +367 | pass +368 | +369 | def b(): | ^^^ E302 -374 | pass -375 | # end +370 | pass +371 | # end | = help: Add missing blank line(s) ℹ Safe fix -370 370 | -371 371 | # comment -372 372 | - 373 |+ -373 374 | def b(): -374 375 | pass -375 376 | # end - -E30.py:382:7: E302 [*] Expected 2 blank lines, found 1 - | -380 | pass -381 | -382 | async def b(): - | ^^^ E302 -383 | pass -384 | # end +366 366 | def a(): +367 367 | pass +368 368 | + 369 |+ +369 370 | def b(): +370 371 | pass +371 372 | # end + +E30.py:380:1: E302 [*] Expected 2 blank lines, found 1 + | +378 | # comment +379 | +380 | def b(): + | ^^^ E302 +381 | pass +382 | # end | = help: Add missing blank line(s) ℹ Safe fix -379 379 | def a(): -380 380 | pass -381 381 | - 382 |+ -382 383 | async def b(): -383 384 | pass -384 385 | # end - -E30.py:391:8: E302 [*] Expected 2 blank lines, found 1 - | -389 | pass -390 | -391 | async def x(y: int = 1): - | ^^^ E302 -392 | pass -393 | # end +377 377 | +378 378 | # comment +379 379 | + 380 |+ +380 381 | def b(): +381 382 | pass +382 383 | # end + +E30.py:389:1: E302 [*] Expected 2 blank lines, found 1 + | +387 | pass +388 | +389 | async def b(): + | ^^^^^ E302 +390 | pass +391 | # end | = help: Add missing blank line(s) ℹ Safe fix -388 388 | async def x(): -389 389 | pass -390 390 | - 391 |+ -391 392 | async def x(y: int = 1): -392 393 | pass -393 394 | # end - -E30.py:399:1: E302 [*] Expected 2 blank lines, found 0 - | -397 | def bar(): -398 | pass -399 | def baz(): pass - | ^^^ E302 +386 386 | def a(): +387 387 | pass +388 388 | + 389 |+ +389 390 | async def b(): +390 391 | pass +391 392 | # end + +E30.py:398:1: E302 [*] Expected 2 blank lines, found 1 + | +396 | pass +397 | +398 | async def x(y: int = 1): + | ^^^^^ E302 +399 | pass 400 | # end | = help: Add missing blank line(s) ℹ Safe fix -396 396 | # E302 -397 397 | def bar(): -398 398 | pass - 399 |+ - 400 |+ -399 401 | def baz(): pass -400 402 | # end -401 403 | - -E30.py:405:1: E302 [*] Expected 2 blank lines, found 0 - | -403 | # E302 -404 | def bar(): pass -405 | def baz(): +395 395 | async def x(): +396 396 | pass +397 397 | + 398 |+ +398 399 | async def x(y: int = 1): +399 400 | pass +400 401 | # end + +E30.py:406:1: E302 [*] Expected 2 blank lines, found 0 + | +404 | def bar(): +405 | pass +406 | def baz(): pass | ^^^ E302 -406 | pass 407 | # end | = help: Add missing blank line(s) ℹ Safe fix -402 402 | 403 403 | # E302 -404 404 | def bar(): pass - 405 |+ +404 404 | def bar(): +405 405 | pass 406 |+ -405 407 | def baz(): -406 408 | pass + 407 |+ +406 408 | def baz(): pass 407 409 | # end +408 410 | -E30.py:415:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:412:1: E302 [*] Expected 2 blank lines, found 0 + | +410 | # E302 +411 | def bar(): pass +412 | def baz(): + | ^^^ E302 +413 | pass +414 | # end | -414 | # comment -415 | @decorator + = help: Add missing blank line(s) + +ℹ Safe fix +409 409 | +410 410 | # E302 +411 411 | def bar(): pass + 412 |+ + 413 |+ +412 414 | def baz(): +413 415 | pass +414 416 | # end + +E30.py:422:1: E302 [*] Expected 2 blank lines, found 0 + | +421 | # comment +422 | @decorator | ^ E302 -416 | def g(): -417 | pass +423 | def g(): +424 | pass | = help: Add missing blank line(s) ℹ Safe fix -412 412 | pass -413 413 | -414 414 | # comment - 415 |+ - 416 |+ -415 417 | @decorator -416 418 | def g(): -417 419 | pass +419 419 | pass +420 420 | +421 421 | # comment + 422 |+ + 423 |+ +422 424 | @decorator +423 425 | def g(): +424 426 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 1f33ed52b7584..fdff7f9c37f62 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,128 +1,128 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:426:5: E303 [*] Too many blank lines (2) +E30.py:433:5: E303 [*] Too many blank lines (2) | -426 | # arbitrary comment +433 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -427 | -428 | def inner(): # E306 not expected +434 | +435 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -422 422 | def fn(): -423 423 | _ = None -424 424 | -425 |- -426 425 | # arbitrary comment -427 426 | -428 427 | def inner(): # E306 not expected +429 429 | def fn(): +430 430 | _ = None +431 431 | +432 |- +433 432 | # arbitrary comment +434 433 | +435 434 | def inner(): # E306 not expected -E30.py:438:5: E303 [*] Too many blank lines (2) +E30.py:445:5: E303 [*] Too many blank lines (2) | -438 | # arbitrary comment +445 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -439 | def inner(): # E306 not expected -440 | pass +446 | def inner(): # E306 not expected +447 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -434 434 | def fn(): -435 435 | _ = None -436 436 | -437 |- -438 437 | # arbitrary comment -439 438 | def inner(): # E306 not expected -440 439 | pass +441 441 | def fn(): +442 442 | _ = None +443 443 | +444 |- +445 444 | # arbitrary comment +446 445 | def inner(): # E306 not expected +447 446 | pass -E30.py:449:1: E303 [*] Too many blank lines (3) +E30.py:456:1: E303 [*] Too many blank lines (3) | -449 | print() +456 | print() | ^^^^^ E303 -450 | # end +457 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -445 445 | print() -446 446 | -447 447 | -448 |- -449 448 | print() -450 449 | # end -451 450 | +452 452 | print() +453 453 | +454 454 | +455 |- +456 455 | print() +457 456 | # end +458 457 | -E30.py:458:1: E303 [*] Too many blank lines (3) +E30.py:465:1: E303 [*] Too many blank lines (3) | -458 | # comment +465 | # comment | ^^^^^^^^^ E303 -459 | -460 | print() +466 | +467 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -454 454 | print() -455 455 | -456 456 | -457 |- -458 457 | # comment -459 458 | -460 459 | print() +461 461 | print() +462 462 | +463 463 | +464 |- +465 464 | # comment +466 465 | +467 466 | print() -E30.py:469:5: E303 [*] Too many blank lines (2) +E30.py:476:5: E303 [*] Too many blank lines (2) | -469 | # comment +476 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -465 465 | def a(): -466 466 | print() -467 467 | -468 |- -469 468 | # comment -470 469 | -471 470 | +472 472 | def a(): +473 473 | print() +474 474 | +475 |- +476 475 | # comment +477 476 | +478 477 | -E30.py:472:5: E303 [*] Too many blank lines (2) +E30.py:479:5: E303 [*] Too many blank lines (2) | -472 | # another comment +479 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -473 | -474 | print() +480 | +481 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -468 468 | -469 469 | # comment -470 470 | -471 |- -472 471 | # another comment -473 472 | -474 473 | print() +475 475 | +476 476 | # comment +477 477 | +478 |- +479 478 | # another comment +480 479 | +481 480 | print() -E30.py:483:1: E303 [*] Too many blank lines (3) +E30.py:490:1: E303 [*] Too many blank lines (3) | -483 | / """This class docstring comes on line 5. -484 | | It gives error E303: too many blank lines (3) -485 | | """ +490 | / """This class docstring comes on line 5. +491 | | It gives error E303: too many blank lines (3) +492 | | """ | |___^ E303 -486 | # end +493 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -479 479 | #!python -480 480 | -481 481 | -482 |- -483 482 | """This class docstring comes on line 5. -484 483 | It gives error E303: too many blank lines (3) -485 484 | """ +486 486 | #!python +487 487 | +488 488 | +489 |- +490 489 | """This class docstring comes on line 5. +491 490 | It gives error E303: too many blank lines (3) +492 491 | """ diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index e5d7273923fd4..45021a7895aa1 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:492:1: E304 [*] blank lines found after function decorator +E30.py:499:1: E304 [*] blank lines found after function decorator | -490 | @decorator -491 | -492 | def function(): +497 | @decorator +498 | +499 | def function(): | ^^^ E304 -493 | pass -494 | # end +500 | pass +501 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -488 488 | -489 489 | # E304 -490 490 | @decorator -491 |- -492 491 | def function(): -493 492 | pass -494 493 | # end +495 495 | +496 496 | # E304 +497 497 | @decorator +498 |- +499 498 | def function(): +500 499 | pass +501 500 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index e47361ed27a32..94ba3406f7dce 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:504:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:511:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -503 | # another comment -504 | fn() +510 | # another comment +511 | fn() | ^^ E305 -505 | # end +512 | # end | = help: Add missing blank line(s) ℹ Safe fix -501 501 | # comment -502 502 | -503 503 | # another comment - 504 |+ - 505 |+ -504 506 | fn() -505 507 | # end -506 508 | +508 508 | # comment +509 509 | +510 510 | # another comment + 511 |+ + 512 |+ +511 513 | fn() +512 514 | # end +513 515 | -E30.py:515:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:522:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -514 | # another comment -515 | a = 1 +521 | # another comment +522 | a = 1 | ^ E305 -516 | # end +523 | # end | = help: Add missing blank line(s) ℹ Safe fix -512 512 | # comment -513 513 | -514 514 | # another comment - 515 |+ - 516 |+ -515 517 | a = 1 -516 518 | # end -517 519 | +519 519 | # comment +520 520 | +521 521 | # another comment + 522 |+ + 523 |+ +522 524 | a = 1 +523 525 | # end +524 526 | -E30.py:527:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:534:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -525 | # another comment -526 | -527 | try: +532 | # another comment +533 | +534 | try: | ^^^ E305 -528 | fn() -529 | except Exception: +535 | fn() +536 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -524 524 | -525 525 | # another comment -526 526 | - 527 |+ -527 528 | try: -528 529 | fn() -529 530 | except Exception: +531 531 | +532 532 | # another comment +533 533 | + 534 |+ +534 535 | try: +535 536 | fn() +536 537 | except Exception: -E30.py:539:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:546:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -538 | # Two spaces before comments, too. -539 | if a(): +545 | # Two spaces before comments, too. +546 | if a(): | ^^ E305 -540 | a() -541 | # end +547 | a() +548 | # end | = help: Add missing blank line(s) ℹ Safe fix -536 536 | print -537 537 | -538 538 | # Two spaces before comments, too. - 539 |+ - 540 |+ -539 541 | if a(): -540 542 | a() -541 543 | # end +543 543 | print +544 544 | +545 545 | # Two spaces before comments, too. + 546 |+ + 547 |+ +546 548 | if a(): +547 549 | a() +548 550 | # end -E30.py:552:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:559:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -550 | blah, blah -551 | -552 | if __name__ == '__main__': +557 | blah, blah +558 | +559 | if __name__ == '__main__': | ^^ E305 -553 | main() -554 | # end +560 | main() +561 | # end | = help: Add missing blank line(s) ℹ Safe fix -549 549 | def main(): -550 550 | blah, blah -551 551 | - 552 |+ -552 553 | if __name__ == '__main__': -553 554 | main() -554 555 | # end +556 556 | def main(): +557 557 | blah, blah +558 558 | + 559 |+ +559 560 | if __name__ == '__main__': +560 561 | main() +561 562 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index a5184464f9869..673cbe4ed9097 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:560:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:567:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -558 | def a(): -559 | x = 1 -560 | def b(): +565 | def a(): +566 | x = 1 +567 | def b(): | ^^^ E306 -561 | pass -562 | # end +568 | pass +569 | # end | = help: Add missing blank line ℹ Safe fix -557 557 | # E306:3:5 -558 558 | def a(): -559 559 | x = 1 - 560 |+ -560 561 | def b(): -561 562 | pass -562 563 | # end - -E30.py:568:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -566 | async def a(): -567 | x = 1 -568 | def b(): +564 564 | # E306:3:5 +565 565 | def a(): +566 566 | x = 1 + 567 |+ +567 568 | def b(): +568 569 | pass +569 570 | # end + +E30.py:575:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +573 | async def a(): +574 | x = 1 +575 | def b(): | ^^^ E306 -569 | pass -570 | # end +576 | pass +577 | # end | = help: Add missing blank line ℹ Safe fix -565 565 | #: E306:3:5 -566 566 | async def a(): -567 567 | x = 1 - 568 |+ -568 569 | def b(): -569 570 | pass -570 571 | # end - -E30.py:576:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -574 | def a(): -575 | x = 2 -576 | def b(): +572 572 | #: E306:3:5 +573 573 | async def a(): +574 574 | x = 1 + 575 |+ +575 576 | def b(): +576 577 | pass +577 578 | # end + +E30.py:583:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +581 | def a(): +582 | x = 2 +583 | def b(): | ^^^ E306 -577 | x = 1 -578 | def c(): +584 | x = 1 +585 | def c(): | = help: Add missing blank line ℹ Safe fix -573 573 | #: E306:3:5 E306:5:9 -574 574 | def a(): -575 575 | x = 2 - 576 |+ -576 577 | def b(): -577 578 | x = 1 -578 579 | def c(): - -E30.py:578:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -576 | def b(): -577 | x = 1 -578 | def c(): +580 580 | #: E306:3:5 E306:5:9 +581 581 | def a(): +582 582 | x = 2 + 583 |+ +583 584 | def b(): +584 585 | x = 1 +585 586 | def c(): + +E30.py:585:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +583 | def b(): +584 | x = 1 +585 | def c(): | ^^^ E306 -579 | pass -580 | # end +586 | pass +587 | # end | = help: Add missing blank line ℹ Safe fix -575 575 | x = 2 -576 576 | def b(): -577 577 | x = 1 - 578 |+ -578 579 | def c(): -579 580 | pass -580 581 | # end - -E30.py:586:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -584 | def a(): -585 | x = 1 -586 | class C: +582 582 | x = 2 +583 583 | def b(): +584 584 | x = 1 + 585 |+ +585 586 | def c(): +586 587 | pass +587 588 | # end + +E30.py:593:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +591 | def a(): +592 | x = 1 +593 | class C: | ^^^^^ E306 -587 | pass -588 | x = 2 +594 | pass +595 | x = 2 | = help: Add missing blank line ℹ Safe fix -583 583 | # E306:3:5 E306:6:5 -584 584 | def a(): -585 585 | x = 1 - 586 |+ -586 587 | class C: -587 588 | pass -588 589 | x = 2 - -E30.py:589:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -587 | pass -588 | x = 2 -589 | def b(): +590 590 | # E306:3:5 E306:6:5 +591 591 | def a(): +592 592 | x = 1 + 593 |+ +593 594 | class C: +594 595 | pass +595 596 | x = 2 + +E30.py:596:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +594 | pass +595 | x = 2 +596 | def b(): | ^^^ E306 -590 | pass -591 | # end +597 | pass +598 | # end | = help: Add missing blank line ℹ Safe fix -586 586 | class C: -587 587 | pass -588 588 | x = 2 - 589 |+ -589 590 | def b(): -590 591 | pass -591 592 | # end +593 593 | class C: +594 594 | pass +595 595 | x = 2 + 596 |+ +596 597 | def b(): +597 598 | pass +598 599 | # end -E30.py:598:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:605:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -596 | def bar(): -597 | pass -598 | def baz(): pass +603 | def bar(): +604 | pass +605 | def baz(): pass | ^^^ E306 -599 | # end +606 | # end | = help: Add missing blank line ℹ Safe fix -595 595 | def foo(): -596 596 | def bar(): -597 597 | pass - 598 |+ -598 599 | def baz(): pass -599 600 | # end -600 601 | +602 602 | def foo(): +603 603 | def bar(): +604 604 | pass + 605 |+ +605 606 | def baz(): pass +606 607 | # end +607 608 | -E30.py:605:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:612:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -603 | def foo(): -604 | def bar(): pass -605 | def baz(): +610 | def foo(): +611 | def bar(): pass +612 | def baz(): | ^^^ E306 -606 | pass -607 | # end +613 | pass +614 | # end | = help: Add missing blank line ℹ Safe fix -602 602 | # E306:3:5 -603 603 | def foo(): -604 604 | def bar(): pass - 605 |+ -605 606 | def baz(): -606 607 | pass -607 608 | # end +609 609 | # E306:3:5 +610 610 | def foo(): +611 611 | def bar(): pass + 612 |+ +612 613 | def baz(): +613 614 | pass +614 615 | # end From 7de1e0584653f682090099dc5561829d0be8c52c Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 18 Nov 2023 19:41:55 +0900 Subject: [PATCH 023/122] Fix E302 error message. --- .../rules/pycodestyle/rules/logical_lines/blank_lines.rs | 6 ++++-- ...ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 91f90cd1abecc..ef17967336c24 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -468,8 +468,10 @@ pub(crate) fn blank_lines( && line.line.blank_lines < BlankLinesConfig::TOP_LEVEL { // E302 - let mut diagnostic = - Diagnostic::new(BlankLinesTopLevel(line.line.blank_lines), token.range); + let mut diagnostic = Diagnostic::new( + BlankLinesTopLevel(line.line.preceding_blank_lines), + token.range, + ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist .line_ending() diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index a5a9a83bd17ed..1b0bc49ade6ba 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -164,7 +164,7 @@ E30.py:412:1: E302 [*] Expected 2 blank lines, found 0 413 415 | pass 414 416 | # end -E30.py:422:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:422:1: E302 [*] Expected 2 blank lines, found 1 | 421 | # comment 422 | @decorator From 0c1450a75e163b643db1c5663103186f8c968246 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 18 Nov 2023 22:03:50 +0900 Subject: [PATCH 024/122] Improve E302's fix interaction with comments. Make the comment stick to the following line instead of the preceding one. --- .../rules/logical_lines/blank_lines.rs | 12 +++++++++++- ...__rules__pycodestyle__tests__E302_E30.py.snap | 16 ++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index ef17967336c24..0c15dde0c90a3 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -31,6 +31,7 @@ pub(crate) struct BlankLinesTrackingVars { /// false when a comment is set dedented, but E305 should trigger on the next non-comment line. follows_comment_after_fn: bool, follows_comment_after_class: bool, + last_non_comment_line_end: TextSize, } impl Default for BlankLinesTrackingVars { @@ -45,6 +46,7 @@ impl Default for BlankLinesTrackingVars { is_first_logical_line: true, follows_comment_after_fn: false, follows_comment_after_class: false, + last_non_comment_line_end: TextSize::new(0), } } } @@ -478,7 +480,7 @@ pub(crate) fn blank_lines( .as_str() .to_string() .repeat((BlankLinesConfig::TOP_LEVEL - line.line.blank_lines) as usize), - locator.line_start(token.range.start()), + locator.line_start(tracked_vars.last_non_comment_line_end), ))); context.push_diagnostic(diagnostic); @@ -604,4 +606,12 @@ pub(crate) fn blank_lines( if tracked_vars.is_first_logical_line && !line_is_comment_only { tracked_vars.is_first_logical_line = false; } + if !line_is_comment_only { + tracked_vars.last_non_comment_line_end = line + .tokens() + .last() + .expect("Line to contain at least one token.") + .range + .end(); + } } diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 1b0bc49ade6ba..a62cfc18b7b96 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -75,13 +75,13 @@ E30.py:380:1: E302 [*] Expected 2 blank lines, found 1 = help: Add missing blank line(s) ℹ Safe fix +375 375 | def a(): +376 376 | pass 377 377 | -378 378 | # comment -379 379 | - 380 |+ + 378 |+ +378 379 | # comment +379 380 | 380 381 | def b(): -381 382 | pass -382 383 | # end E30.py:389:1: E302 [*] Expected 2 blank lines, found 1 | @@ -175,13 +175,13 @@ E30.py:422:1: E302 [*] Expected 2 blank lines, found 1 = help: Add missing blank line(s) ℹ Safe fix +418 418 | def f(): 419 419 | pass 420 420 | -421 421 | # comment + 421 |+ 422 |+ - 423 |+ +421 423 | # comment 422 424 | @decorator 423 425 | def g(): -424 426 | pass From 7b9b277d80f537590c61ee0eb8019a1972938d14 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 18 Nov 2023 22:22:56 +0900 Subject: [PATCH 025/122] Fix E301 regression and improve E301 fix. --- .../rules/logical_lines/blank_lines.rs | 14 +++++++------ ...ules__pycodestyle__tests__E301_E30.py.snap | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 0c15dde0c90a3..ada42679f9cdc 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -31,6 +31,8 @@ pub(crate) struct BlankLinesTrackingVars { /// false when a comment is set dedented, but E305 should trigger on the next non-comment line. follows_comment_after_fn: bool, follows_comment_after_class: bool, + /// Used for the fix in case a comment separates two non-comment logical lines to make the comment "stick" + /// to the second line instead of the first. last_non_comment_line_end: TextSize, } @@ -345,9 +347,11 @@ fn is_decorator(line: Option<&LogicalLine>) -> bool { /// Returns `true` if line is a docstring only line. fn is_docstring(line: Option<&LogicalLine>) -> bool { line.is_some_and(|line| { - line.tokens_trimmed() - .iter() - .all(|token| matches!(token.kind(), TokenKind::String)) + line.tokens_trimmed().len() > 0 + && line + .tokens_trimmed() + .iter() + .all(|token| matches!(token.kind(), TokenKind::String)) }) } @@ -437,11 +441,9 @@ pub(crate) fn blank_lines( // E301 let mut diagnostic = Diagnostic::new(BlankLineBetweenMethods(line.line.blank_lines), token.range); - // TODO: In the case where there is a comment between two methods, make the comment "stick" - // to the second method instead of the first. diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().as_str().to_string(), - locator.line_start(token.range.start()), + locator.line_start(tracked_vars.last_non_comment_line_end), ))); context.push_diagnostic(diagnostic); } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async) diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index b0fb810596f30..ee59cc4223b31 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -21,4 +21,24 @@ E30.py:335:5: E301 [*] Expected 1 blank line, found 0 336 337 | pass 337 338 | # end +E30.py:346:5: E301 [*] Expected 1 blank line, found 0 + | +344 | pass +345 | # comment +346 | def fn2(): + | ^^^ E301 +347 | pass +348 | # end + | + = help: Add missing blank line(s) + +ℹ Safe fix +342 342 | +343 343 | def fn1(): +344 344 | pass + 345 |+ +345 346 | # comment +346 347 | def fn2(): +347 348 | pass + From 1d7b2353782b4fb9d70775655718be0fff652369 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 18 Nov 2023 22:31:45 +0900 Subject: [PATCH 026/122] Fix E302 fix's interaction with comments. --- .../rules/pycodestyle/rules/logical_lines/blank_lines.rs | 8 +++----- ...ff_linter__rules__pycodestyle__tests__E302_E30.py.snap | 7 +++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index ada42679f9cdc..8da8e359d0cb2 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -477,11 +477,9 @@ pub(crate) fn blank_lines( token.range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .as_str() - .to_string() - .repeat((BlankLinesConfig::TOP_LEVEL - line.line.blank_lines) as usize), + stylist.line_ending().as_str().to_string().repeat( + (BlankLinesConfig::TOP_LEVEL - line.line.preceding_blank_lines) as usize, + ), locator.line_start(tracked_vars.last_non_comment_line_end), ))); diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index a62cfc18b7b96..9ead7175e3883 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -179,9 +179,8 @@ E30.py:422:1: E302 [*] Expected 2 blank lines, found 1 419 419 | pass 420 420 | 421 |+ - 422 |+ -421 423 | # comment -422 424 | @decorator -423 425 | def g(): +421 422 | # comment +422 423 | @decorator +423 424 | def g(): From 73e5ba9f70e547bb208cbc50dc0af7e0cc2d0943 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 18 Nov 2023 22:38:14 +0900 Subject: [PATCH 027/122] Clippy fix. --- .../src/rules/pycodestyle/rules/logical_lines/blank_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 8da8e359d0cb2..8c29f260244f7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -347,7 +347,7 @@ fn is_decorator(line: Option<&LogicalLine>) -> bool { /// Returns `true` if line is a docstring only line. fn is_docstring(line: Option<&LogicalLine>) -> bool { line.is_some_and(|line| { - line.tokens_trimmed().len() > 0 + !line.tokens_trimmed().is_empty() && line .tokens_trimmed() .iter() From c1a7a7c0071438830f931b63fed3077a420a5751 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 19 Nov 2023 14:42:26 +0900 Subject: [PATCH 028/122] Fix docstring + comment leading to false positive. --- .../test/fixtures/pycodestyle/E30.py | 10 + .../rules/logical_lines/blank_lines.rs | 17 +- ...ules__pycodestyle__tests__E301_E30.py.snap | 52 ++-- ...ules__pycodestyle__tests__E302_E30.py.snap | 270 +++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 150 +++++----- ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 ++++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 224 +++++++-------- 8 files changed, 445 insertions(+), 430 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index f65760b609f95..e5dfa125d4cbf 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -327,6 +327,16 @@ async def function(data: None) -> None: # end +# no error +class Class: + def method(): + """docstring""" + # comment + def function(): + pass +# end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 8c29f260244f7..e77899a206e00 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -19,6 +19,7 @@ use super::LogicalLine; pub(crate) struct BlankLinesTrackingVars { follows_decorator: bool, follows_def: bool, + follows_docstring: bool, is_in_class: bool, /// The indent level where the class started. class_indent_level: usize, @@ -41,6 +42,7 @@ impl Default for BlankLinesTrackingVars { BlankLinesTrackingVars { follows_decorator: false, follows_def: false, + follows_docstring: false, is_in_class: false, class_indent_level: 0, is_in_fn: false, @@ -419,8 +421,8 @@ pub(crate) fn blank_lines( } else if token.kind() == TokenKind::Def // Only applies to method. && tracked_vars.is_in_class - // The class's docstring can directly precede the first function. - && !is_docstring(prev_line) + // The class/parent method's docstring can directly precede the def. + && !tracked_vars.follows_docstring // Do not trigger when then def follows an if/while/etc... && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) && ( @@ -544,7 +546,7 @@ pub(crate) fn blank_lines( && line.line.preceding_blank_lines == 0 && !is_decorator(prev_line) // The class's docstring can directly precede the first function. - && !is_docstring(prev_line) + && !tracked_vars.follows_docstring && !prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level < indent_level) // Allow groups of one-liners. && !(tracked_vars.follows_def @@ -603,10 +605,13 @@ pub(crate) fn blank_lines( } } - if tracked_vars.is_first_logical_line && !line_is_comment_only { - tracked_vars.is_first_logical_line = false; - } if !line_is_comment_only { + if tracked_vars.is_first_logical_line { + tracked_vars.is_first_logical_line = false; + } + + tracked_vars.follows_docstring = is_docstring(Some(line)); + tracked_vars.last_non_comment_line_end = line .tokens() .last() diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index ee59cc4223b31..913a2973a8fba 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:335:5: E301 [*] Expected 1 blank line, found 0 +E30.py:345:5: E301 [*] Expected 1 blank line, found 0 | -333 | def func1(): -334 | pass -335 | def func2(): +343 | def func1(): +344 | pass +345 | def func2(): | ^^^ E301 -336 | pass -337 | # end +346 | pass +347 | # end | = help: Add missing blank line(s) ℹ Safe fix -332 332 | -333 333 | def func1(): -334 334 | pass - 335 |+ -335 336 | def func2(): -336 337 | pass -337 338 | # end +342 342 | +343 343 | def func1(): +344 344 | pass + 345 |+ +345 346 | def func2(): +346 347 | pass +347 348 | # end -E30.py:346:5: E301 [*] Expected 1 blank line, found 0 +E30.py:356:5: E301 [*] Expected 1 blank line, found 0 | -344 | pass -345 | # comment -346 | def fn2(): +354 | pass +355 | # comment +356 | def fn2(): | ^^^ E301 -347 | pass -348 | # end +357 | pass +358 | # end | = help: Add missing blank line(s) ℹ Safe fix -342 342 | -343 343 | def fn1(): -344 344 | pass - 345 |+ -345 346 | # comment -346 347 | def fn2(): -347 348 | pass +352 352 | +353 353 | def fn1(): +354 354 | pass + 355 |+ +355 356 | # comment +356 357 | def fn2(): +357 358 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 9ead7175e3883..16515241053af 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,186 +1,186 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:353:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:363:1: E302 [*] Expected 2 blank lines, found 0 | -351 | # E302 -352 | """Main module.""" -353 | def fn(): +361 | # E302 +362 | """Main module.""" +363 | def fn(): | ^^^ E302 -354 | pass -355 | # end +364 | pass +365 | # end | = help: Add missing blank line(s) ℹ Safe fix -350 350 | -351 351 | # E302 -352 352 | """Main module.""" - 353 |+ - 354 |+ -353 355 | def fn(): -354 356 | pass -355 357 | # end - -E30.py:360:1: E302 [*] Expected 2 blank lines, found 0 - | -358 | # E302 -359 | import sys -360 | def get_sys_path(): +360 360 | +361 361 | # E302 +362 362 | """Main module.""" + 363 |+ + 364 |+ +363 365 | def fn(): +364 366 | pass +365 367 | # end + +E30.py:370:1: E302 [*] Expected 2 blank lines, found 0 + | +368 | # E302 +369 | import sys +370 | def get_sys_path(): | ^^^ E302 -361 | return sys.path -362 | # end +371 | return sys.path +372 | # end | = help: Add missing blank line(s) ℹ Safe fix -357 357 | -358 358 | # E302 -359 359 | import sys - 360 |+ - 361 |+ -360 362 | def get_sys_path(): -361 363 | return sys.path -362 364 | # end - -E30.py:369:1: E302 [*] Expected 2 blank lines, found 1 - | -367 | pass -368 | -369 | def b(): +367 367 | +368 368 | # E302 +369 369 | import sys + 370 |+ + 371 |+ +370 372 | def get_sys_path(): +371 373 | return sys.path +372 374 | # end + +E30.py:379:1: E302 [*] Expected 2 blank lines, found 1 + | +377 | pass +378 | +379 | def b(): | ^^^ E302 -370 | pass -371 | # end +380 | pass +381 | # end | = help: Add missing blank line(s) ℹ Safe fix -366 366 | def a(): -367 367 | pass -368 368 | - 369 |+ -369 370 | def b(): -370 371 | pass -371 372 | # end - -E30.py:380:1: E302 [*] Expected 2 blank lines, found 1 - | -378 | # comment -379 | -380 | def b(): +376 376 | def a(): +377 377 | pass +378 378 | + 379 |+ +379 380 | def b(): +380 381 | pass +381 382 | # end + +E30.py:390:1: E302 [*] Expected 2 blank lines, found 1 + | +388 | # comment +389 | +390 | def b(): | ^^^ E302 -381 | pass -382 | # end +391 | pass +392 | # end | = help: Add missing blank line(s) ℹ Safe fix -375 375 | def a(): -376 376 | pass -377 377 | - 378 |+ -378 379 | # comment -379 380 | -380 381 | def b(): - -E30.py:389:1: E302 [*] Expected 2 blank lines, found 1 - | -387 | pass -388 | -389 | async def b(): +385 385 | def a(): +386 386 | pass +387 387 | + 388 |+ +388 389 | # comment +389 390 | +390 391 | def b(): + +E30.py:399:1: E302 [*] Expected 2 blank lines, found 1 + | +397 | pass +398 | +399 | async def b(): | ^^^^^ E302 -390 | pass -391 | # end +400 | pass +401 | # end | = help: Add missing blank line(s) ℹ Safe fix -386 386 | def a(): -387 387 | pass -388 388 | - 389 |+ -389 390 | async def b(): -390 391 | pass -391 392 | # end - -E30.py:398:1: E302 [*] Expected 2 blank lines, found 1 - | -396 | pass -397 | -398 | async def x(y: int = 1): +396 396 | def a(): +397 397 | pass +398 398 | + 399 |+ +399 400 | async def b(): +400 401 | pass +401 402 | # end + +E30.py:408:1: E302 [*] Expected 2 blank lines, found 1 + | +406 | pass +407 | +408 | async def x(y: int = 1): | ^^^^^ E302 -399 | pass -400 | # end +409 | pass +410 | # end | = help: Add missing blank line(s) ℹ Safe fix -395 395 | async def x(): -396 396 | pass -397 397 | - 398 |+ -398 399 | async def x(y: int = 1): -399 400 | pass -400 401 | # end - -E30.py:406:1: E302 [*] Expected 2 blank lines, found 0 - | -404 | def bar(): -405 | pass -406 | def baz(): pass +405 405 | async def x(): +406 406 | pass +407 407 | + 408 |+ +408 409 | async def x(y: int = 1): +409 410 | pass +410 411 | # end + +E30.py:416:1: E302 [*] Expected 2 blank lines, found 0 + | +414 | def bar(): +415 | pass +416 | def baz(): pass | ^^^ E302 -407 | # end +417 | # end | = help: Add missing blank line(s) ℹ Safe fix -403 403 | # E302 -404 404 | def bar(): -405 405 | pass - 406 |+ - 407 |+ -406 408 | def baz(): pass -407 409 | # end -408 410 | - -E30.py:412:1: E302 [*] Expected 2 blank lines, found 0 - | -410 | # E302 -411 | def bar(): pass -412 | def baz(): +413 413 | # E302 +414 414 | def bar(): +415 415 | pass + 416 |+ + 417 |+ +416 418 | def baz(): pass +417 419 | # end +418 420 | + +E30.py:422:1: E302 [*] Expected 2 blank lines, found 0 + | +420 | # E302 +421 | def bar(): pass +422 | def baz(): | ^^^ E302 -413 | pass -414 | # end +423 | pass +424 | # end | = help: Add missing blank line(s) ℹ Safe fix -409 409 | -410 410 | # E302 -411 411 | def bar(): pass - 412 |+ - 413 |+ -412 414 | def baz(): -413 415 | pass -414 416 | # end - -E30.py:422:1: E302 [*] Expected 2 blank lines, found 1 - | -421 | # comment -422 | @decorator +419 419 | +420 420 | # E302 +421 421 | def bar(): pass + 422 |+ + 423 |+ +422 424 | def baz(): +423 425 | pass +424 426 | # end + +E30.py:432:1: E302 [*] Expected 2 blank lines, found 1 + | +431 | # comment +432 | @decorator | ^ E302 -423 | def g(): -424 | pass +433 | def g(): +434 | pass | = help: Add missing blank line(s) ℹ Safe fix -418 418 | def f(): -419 419 | pass -420 420 | - 421 |+ -421 422 | # comment -422 423 | @decorator -423 424 | def g(): +428 428 | def f(): +429 429 | pass +430 430 | + 431 |+ +431 432 | # comment +432 433 | @decorator +433 434 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index fdff7f9c37f62..8c44bb45d05b4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,128 +1,128 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:433:5: E303 [*] Too many blank lines (2) +E30.py:443:5: E303 [*] Too many blank lines (2) | -433 | # arbitrary comment +443 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -434 | -435 | def inner(): # E306 not expected +444 | +445 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -429 429 | def fn(): -430 430 | _ = None -431 431 | -432 |- -433 432 | # arbitrary comment -434 433 | -435 434 | def inner(): # E306 not expected +439 439 | def fn(): +440 440 | _ = None +441 441 | +442 |- +443 442 | # arbitrary comment +444 443 | +445 444 | def inner(): # E306 not expected -E30.py:445:5: E303 [*] Too many blank lines (2) +E30.py:455:5: E303 [*] Too many blank lines (2) | -445 | # arbitrary comment +455 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -446 | def inner(): # E306 not expected -447 | pass +456 | def inner(): # E306 not expected +457 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -441 441 | def fn(): -442 442 | _ = None -443 443 | -444 |- -445 444 | # arbitrary comment -446 445 | def inner(): # E306 not expected -447 446 | pass +451 451 | def fn(): +452 452 | _ = None +453 453 | +454 |- +455 454 | # arbitrary comment +456 455 | def inner(): # E306 not expected +457 456 | pass -E30.py:456:1: E303 [*] Too many blank lines (3) +E30.py:466:1: E303 [*] Too many blank lines (3) | -456 | print() +466 | print() | ^^^^^ E303 -457 | # end +467 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -452 452 | print() -453 453 | -454 454 | -455 |- -456 455 | print() -457 456 | # end -458 457 | +462 462 | print() +463 463 | +464 464 | +465 |- +466 465 | print() +467 466 | # end +468 467 | -E30.py:465:1: E303 [*] Too many blank lines (3) +E30.py:475:1: E303 [*] Too many blank lines (3) | -465 | # comment +475 | # comment | ^^^^^^^^^ E303 -466 | -467 | print() +476 | +477 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -461 461 | print() -462 462 | -463 463 | -464 |- -465 464 | # comment -466 465 | -467 466 | print() +471 471 | print() +472 472 | +473 473 | +474 |- +475 474 | # comment +476 475 | +477 476 | print() -E30.py:476:5: E303 [*] Too many blank lines (2) +E30.py:486:5: E303 [*] Too many blank lines (2) | -476 | # comment +486 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -472 472 | def a(): -473 473 | print() -474 474 | -475 |- -476 475 | # comment -477 476 | -478 477 | +482 482 | def a(): +483 483 | print() +484 484 | +485 |- +486 485 | # comment +487 486 | +488 487 | -E30.py:479:5: E303 [*] Too many blank lines (2) +E30.py:489:5: E303 [*] Too many blank lines (2) | -479 | # another comment +489 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -480 | -481 | print() +490 | +491 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -475 475 | -476 476 | # comment -477 477 | -478 |- -479 478 | # another comment -480 479 | -481 480 | print() +485 485 | +486 486 | # comment +487 487 | +488 |- +489 488 | # another comment +490 489 | +491 490 | print() -E30.py:490:1: E303 [*] Too many blank lines (3) +E30.py:500:1: E303 [*] Too many blank lines (3) | -490 | / """This class docstring comes on line 5. -491 | | It gives error E303: too many blank lines (3) -492 | | """ +500 | / """This class docstring comes on line 5. +501 | | It gives error E303: too many blank lines (3) +502 | | """ | |___^ E303 -493 | # end +503 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -486 486 | #!python -487 487 | -488 488 | -489 |- -490 489 | """This class docstring comes on line 5. -491 490 | It gives error E303: too many blank lines (3) -492 491 | """ +496 496 | #!python +497 497 | +498 498 | +499 |- +500 499 | """This class docstring comes on line 5. +501 500 | It gives error E303: too many blank lines (3) +502 501 | """ diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 45021a7895aa1..3d3e73ce7e388 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:499:1: E304 [*] blank lines found after function decorator +E30.py:509:1: E304 [*] blank lines found after function decorator | -497 | @decorator -498 | -499 | def function(): +507 | @decorator +508 | +509 | def function(): | ^^^ E304 -500 | pass -501 | # end +510 | pass +511 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -495 495 | -496 496 | # E304 -497 497 | @decorator -498 |- -499 498 | def function(): -500 499 | pass -501 500 | # end +505 505 | +506 506 | # E304 +507 507 | @decorator +508 |- +509 508 | def function(): +510 509 | pass +511 510 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 94ba3406f7dce..970f602afe712 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:511:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:521:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -510 | # another comment -511 | fn() +520 | # another comment +521 | fn() | ^^ E305 -512 | # end +522 | # end | = help: Add missing blank line(s) ℹ Safe fix -508 508 | # comment -509 509 | -510 510 | # another comment - 511 |+ - 512 |+ -511 513 | fn() -512 514 | # end -513 515 | +518 518 | # comment +519 519 | +520 520 | # another comment + 521 |+ + 522 |+ +521 523 | fn() +522 524 | # end +523 525 | -E30.py:522:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:532:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -521 | # another comment -522 | a = 1 +531 | # another comment +532 | a = 1 | ^ E305 -523 | # end +533 | # end | = help: Add missing blank line(s) ℹ Safe fix -519 519 | # comment -520 520 | -521 521 | # another comment - 522 |+ - 523 |+ -522 524 | a = 1 -523 525 | # end -524 526 | +529 529 | # comment +530 530 | +531 531 | # another comment + 532 |+ + 533 |+ +532 534 | a = 1 +533 535 | # end +534 536 | -E30.py:534:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:544:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -532 | # another comment -533 | -534 | try: +542 | # another comment +543 | +544 | try: | ^^^ E305 -535 | fn() -536 | except Exception: +545 | fn() +546 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -531 531 | -532 532 | # another comment -533 533 | - 534 |+ -534 535 | try: -535 536 | fn() -536 537 | except Exception: +541 541 | +542 542 | # another comment +543 543 | + 544 |+ +544 545 | try: +545 546 | fn() +546 547 | except Exception: -E30.py:546:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:556:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -545 | # Two spaces before comments, too. -546 | if a(): +555 | # Two spaces before comments, too. +556 | if a(): | ^^ E305 -547 | a() -548 | # end +557 | a() +558 | # end | = help: Add missing blank line(s) ℹ Safe fix -543 543 | print -544 544 | -545 545 | # Two spaces before comments, too. - 546 |+ - 547 |+ -546 548 | if a(): -547 549 | a() -548 550 | # end +553 553 | print +554 554 | +555 555 | # Two spaces before comments, too. + 556 |+ + 557 |+ +556 558 | if a(): +557 559 | a() +558 560 | # end -E30.py:559:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:569:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -557 | blah, blah -558 | -559 | if __name__ == '__main__': +567 | blah, blah +568 | +569 | if __name__ == '__main__': | ^^ E305 -560 | main() -561 | # end +570 | main() +571 | # end | = help: Add missing blank line(s) ℹ Safe fix -556 556 | def main(): -557 557 | blah, blah -558 558 | - 559 |+ -559 560 | if __name__ == '__main__': -560 561 | main() -561 562 | # end +566 566 | def main(): +567 567 | blah, blah +568 568 | + 569 |+ +569 570 | if __name__ == '__main__': +570 571 | main() +571 572 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 673cbe4ed9097..f4a1e714532bf 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:567:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:577:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -565 | def a(): -566 | x = 1 -567 | def b(): +575 | def a(): +576 | x = 1 +577 | def b(): | ^^^ E306 -568 | pass -569 | # end +578 | pass +579 | # end | = help: Add missing blank line ℹ Safe fix -564 564 | # E306:3:5 -565 565 | def a(): -566 566 | x = 1 - 567 |+ -567 568 | def b(): -568 569 | pass -569 570 | # end - -E30.py:575:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -573 | async def a(): -574 | x = 1 -575 | def b(): +574 574 | # E306:3:5 +575 575 | def a(): +576 576 | x = 1 + 577 |+ +577 578 | def b(): +578 579 | pass +579 580 | # end + +E30.py:585:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +583 | async def a(): +584 | x = 1 +585 | def b(): | ^^^ E306 -576 | pass -577 | # end - | - = help: Add missing blank line - -ℹ Safe fix -572 572 | #: E306:3:5 -573 573 | async def a(): -574 574 | x = 1 - 575 |+ -575 576 | def b(): -576 577 | pass -577 578 | # end - -E30.py:583:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -581 | def a(): -582 | x = 2 -583 | def b(): - | ^^^ E306 -584 | x = 1 -585 | def c(): - | - = help: Add missing blank line - -ℹ Safe fix -580 580 | #: E306:3:5 E306:5:9 -581 581 | def a(): -582 582 | x = 2 - 583 |+ -583 584 | def b(): -584 585 | x = 1 -585 586 | def c(): - -E30.py:585:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -583 | def b(): -584 | x = 1 -585 | def c(): - | ^^^ E306 -586 | pass +586 | pass 587 | # end | = help: Add missing blank line ℹ Safe fix -582 582 | x = 2 -583 583 | def b(): -584 584 | x = 1 +582 582 | #: E306:3:5 +583 583 | async def a(): +584 584 | x = 1 585 |+ -585 586 | def c(): -586 587 | pass +585 586 | def b(): +586 587 | pass 587 588 | # end E30.py:593:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | 591 | def a(): -592 | x = 1 -593 | class C: - | ^^^^^ E306 -594 | pass -595 | x = 2 +592 | x = 2 +593 | def b(): + | ^^^ E306 +594 | x = 1 +595 | def c(): | = help: Add missing blank line ℹ Safe fix -590 590 | # E306:3:5 E306:6:5 +590 590 | #: E306:3:5 E306:5:9 591 591 | def a(): -592 592 | x = 1 +592 592 | x = 2 593 |+ -593 594 | class C: -594 595 | pass -595 596 | x = 2 +593 594 | def b(): +594 595 | x = 1 +595 596 | def c(): -E30.py:596:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:595:9: E306 [*] Expected 1 blank line before a nested definition, found 0 | -594 | pass -595 | x = 2 -596 | def b(): - | ^^^ E306 -597 | pass -598 | # end +593 | def b(): +594 | x = 1 +595 | def c(): + | ^^^ E306 +596 | pass +597 | # end + | + = help: Add missing blank line + +ℹ Safe fix +592 592 | x = 2 +593 593 | def b(): +594 594 | x = 1 + 595 |+ +595 596 | def c(): +596 597 | pass +597 598 | # end + +E30.py:603:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +601 | def a(): +602 | x = 1 +603 | class C: + | ^^^^^ E306 +604 | pass +605 | x = 2 | = help: Add missing blank line ℹ Safe fix -593 593 | class C: -594 594 | pass -595 595 | x = 2 - 596 |+ -596 597 | def b(): -597 598 | pass -598 599 | # end +600 600 | # E306:3:5 E306:6:5 +601 601 | def a(): +602 602 | x = 1 + 603 |+ +603 604 | class C: +604 605 | pass +605 606 | x = 2 -E30.py:605:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:606:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -603 | def bar(): 604 | pass -605 | def baz(): pass +605 | x = 2 +606 | def b(): | ^^^ E306 -606 | # end +607 | pass +608 | # end | = help: Add missing blank line ℹ Safe fix -602 602 | def foo(): -603 603 | def bar(): +603 603 | class C: 604 604 | pass - 605 |+ -605 606 | def baz(): pass -606 607 | # end -607 608 | +605 605 | x = 2 + 606 |+ +606 607 | def b(): +607 608 | pass +608 609 | # end -E30.py:612:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:615:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +613 | def bar(): +614 | pass +615 | def baz(): pass + | ^^^ E306 +616 | # end | -610 | def foo(): -611 | def bar(): pass -612 | def baz(): + = help: Add missing blank line + +ℹ Safe fix +612 612 | def foo(): +613 613 | def bar(): +614 614 | pass + 615 |+ +615 616 | def baz(): pass +616 617 | # end +617 618 | + +E30.py:622:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +620 | def foo(): +621 | def bar(): pass +622 | def baz(): | ^^^ E306 -613 | pass -614 | # end +623 | pass +624 | # end | = help: Add missing blank line ℹ Safe fix -609 609 | # E306:3:5 -610 610 | def foo(): -611 611 | def bar(): pass - 612 |+ -612 613 | def baz(): -613 614 | pass -614 615 | # end +619 619 | # E306:3:5 +620 620 | def foo(): +621 621 | def bar(): pass + 622 |+ +622 623 | def baz(): +623 624 | pass +624 625 | # end From b484ca4a1267cda1a90f950f32d59c0be679b0f7 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 19 Nov 2023 19:59:21 +0900 Subject: [PATCH 029/122] Fix E305 false positive for indented classes/defs --- .../test/fixtures/pycodestyle/E30.py | 12 + .../rules/logical_lines/blank_lines.rs | 42 ++- ...ules__pycodestyle__tests__E301_E30.py.snap | 52 ++-- ...ules__pycodestyle__tests__E302_E30.py.snap | 270 +++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 150 +++++----- ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 ++++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 234 +++++++-------- 8 files changed, 469 insertions(+), 443 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index e5dfa125d4cbf..5cc5362d6cd52 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -337,6 +337,18 @@ def function(): # end +# no error +try: + if True: + # comment + class Class: + pass + +except: + pass +# end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index e77899a206e00..444617f0f4f94 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -35,6 +35,7 @@ pub(crate) struct BlankLinesTrackingVars { /// Used for the fix in case a comment separates two non-comment logical lines to make the comment "stick" /// to the second line instead of the first. last_non_comment_line_end: TextSize, + previous_unindented_token: Option, } impl Default for BlankLinesTrackingVars { @@ -51,6 +52,7 @@ impl Default for BlankLinesTrackingVars { follows_comment_after_fn: false, follows_comment_after_class: false, last_non_comment_line_end: TextSize::new(0), + previous_unindented_token: None, } } } @@ -357,6 +359,21 @@ fn is_docstring(line: Option<&LogicalLine>) -> bool { }) } +/// Returns `true` if the token is Async, Class or Def +fn is_top_level_token(token: Option) -> bool { + token.is_some_and(|token| matches!(token, TokenKind::Class | TokenKind::Def | TokenKind::Async)) +} + +/// Returns `true` if the token is At, Async, Class or Def +fn is_top_level_token_or_decorator(token: Option) -> bool { + token.is_some_and(|token| { + matches!( + token, + TokenKind::Class | TokenKind::Def | TokenKind::Async | TokenKind::At + ) + }) +} + /// E301, E302, E303, E304, E305, E306 #[allow(clippy::too_many_arguments)] pub(crate) fn blank_lines( @@ -372,13 +389,10 @@ pub(crate) fn blank_lines( ) { let line_is_comment_only = line.is_comment_only(); - let mut follows_class_or_fn = false; if indent_level < tracked_vars.class_indent_level && tracked_vars.is_in_class { tracked_vars.is_in_class = false; if line_is_comment_only { tracked_vars.follows_comment_after_class = true; - } else { - follows_class_or_fn = true; } } @@ -386,8 +400,6 @@ pub(crate) fn blank_lines( tracked_vars.is_in_fn = false; if line_is_comment_only { tracked_vars.follows_comment_after_fn = true; - } else { - follows_class_or_fn = true; } } @@ -396,8 +408,6 @@ pub(crate) fn blank_lines( if tracked_vars.follows_comment_after_fn && !line_is_comment_only { if indent_level == tracked_vars.fn_indent_level { tracked_vars.is_in_fn = true; - } else { - follows_class_or_fn = true; } tracked_vars.follows_comment_after_fn = false; } @@ -405,8 +415,6 @@ pub(crate) fn blank_lines( if tracked_vars.follows_comment_after_class && !line_is_comment_only { if indent_level == tracked_vars.class_indent_level { tracked_vars.is_in_class = true; - } else { - follows_class_or_fn = true; } tracked_vars.follows_comment_after_class = false; } @@ -517,13 +525,10 @@ pub(crate) fn blank_lines( context.push_diagnostic(diagnostic); } else if line.line.preceding_blank_lines < BlankLinesConfig::TOP_LEVEL - && follows_class_or_fn + && is_top_level_token(tracked_vars.previous_unindented_token) && indent_level == 0 && !line_is_comment_only - && !matches!( - token.kind(), - TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async - ) + && !is_top_level_token_or_decorator(Some(token.kind)) { // E305 let mut diagnostic = Diagnostic::new( @@ -618,5 +623,14 @@ pub(crate) fn blank_lines( .expect("Line to contain at least one token.") .range .end(); + + if indent_level == 0 && !line.tokens_trimmed().is_empty() { + tracked_vars.previous_unindented_token = Some( + line.tokens_trimmed() + .first() + .expect("Previously checked.") + .kind, + ); + } } } diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index 913a2973a8fba..b5c16e49aa1a0 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:345:5: E301 [*] Expected 1 blank line, found 0 +E30.py:357:5: E301 [*] Expected 1 blank line, found 0 | -343 | def func1(): -344 | pass -345 | def func2(): +355 | def func1(): +356 | pass +357 | def func2(): | ^^^ E301 -346 | pass -347 | # end +358 | pass +359 | # end | = help: Add missing blank line(s) ℹ Safe fix -342 342 | -343 343 | def func1(): -344 344 | pass - 345 |+ -345 346 | def func2(): -346 347 | pass -347 348 | # end +354 354 | +355 355 | def func1(): +356 356 | pass + 357 |+ +357 358 | def func2(): +358 359 | pass +359 360 | # end -E30.py:356:5: E301 [*] Expected 1 blank line, found 0 +E30.py:368:5: E301 [*] Expected 1 blank line, found 0 | -354 | pass -355 | # comment -356 | def fn2(): +366 | pass +367 | # comment +368 | def fn2(): | ^^^ E301 -357 | pass -358 | # end +369 | pass +370 | # end | = help: Add missing blank line(s) ℹ Safe fix -352 352 | -353 353 | def fn1(): -354 354 | pass - 355 |+ -355 356 | # comment -356 357 | def fn2(): -357 358 | pass +364 364 | +365 365 | def fn1(): +366 366 | pass + 367 |+ +367 368 | # comment +368 369 | def fn2(): +369 370 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 16515241053af..dbcfe57ab650f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,186 +1,186 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:363:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:375:1: E302 [*] Expected 2 blank lines, found 0 | -361 | # E302 -362 | """Main module.""" -363 | def fn(): +373 | # E302 +374 | """Main module.""" +375 | def fn(): | ^^^ E302 -364 | pass -365 | # end +376 | pass +377 | # end | = help: Add missing blank line(s) ℹ Safe fix -360 360 | -361 361 | # E302 -362 362 | """Main module.""" - 363 |+ - 364 |+ -363 365 | def fn(): -364 366 | pass -365 367 | # end - -E30.py:370:1: E302 [*] Expected 2 blank lines, found 0 - | -368 | # E302 -369 | import sys -370 | def get_sys_path(): +372 372 | +373 373 | # E302 +374 374 | """Main module.""" + 375 |+ + 376 |+ +375 377 | def fn(): +376 378 | pass +377 379 | # end + +E30.py:382:1: E302 [*] Expected 2 blank lines, found 0 + | +380 | # E302 +381 | import sys +382 | def get_sys_path(): | ^^^ E302 -371 | return sys.path -372 | # end +383 | return sys.path +384 | # end | = help: Add missing blank line(s) ℹ Safe fix -367 367 | -368 368 | # E302 -369 369 | import sys - 370 |+ - 371 |+ -370 372 | def get_sys_path(): -371 373 | return sys.path -372 374 | # end - -E30.py:379:1: E302 [*] Expected 2 blank lines, found 1 - | -377 | pass -378 | -379 | def b(): +379 379 | +380 380 | # E302 +381 381 | import sys + 382 |+ + 383 |+ +382 384 | def get_sys_path(): +383 385 | return sys.path +384 386 | # end + +E30.py:391:1: E302 [*] Expected 2 blank lines, found 1 + | +389 | pass +390 | +391 | def b(): | ^^^ E302 -380 | pass -381 | # end +392 | pass +393 | # end | = help: Add missing blank line(s) ℹ Safe fix -376 376 | def a(): -377 377 | pass -378 378 | - 379 |+ -379 380 | def b(): -380 381 | pass -381 382 | # end - -E30.py:390:1: E302 [*] Expected 2 blank lines, found 1 - | -388 | # comment -389 | -390 | def b(): +388 388 | def a(): +389 389 | pass +390 390 | + 391 |+ +391 392 | def b(): +392 393 | pass +393 394 | # end + +E30.py:402:1: E302 [*] Expected 2 blank lines, found 1 + | +400 | # comment +401 | +402 | def b(): | ^^^ E302 -391 | pass -392 | # end +403 | pass +404 | # end | = help: Add missing blank line(s) ℹ Safe fix -385 385 | def a(): -386 386 | pass -387 387 | - 388 |+ -388 389 | # comment -389 390 | -390 391 | def b(): - -E30.py:399:1: E302 [*] Expected 2 blank lines, found 1 - | -397 | pass -398 | -399 | async def b(): +397 397 | def a(): +398 398 | pass +399 399 | + 400 |+ +400 401 | # comment +401 402 | +402 403 | def b(): + +E30.py:411:1: E302 [*] Expected 2 blank lines, found 1 + | +409 | pass +410 | +411 | async def b(): | ^^^^^ E302 -400 | pass -401 | # end +412 | pass +413 | # end | = help: Add missing blank line(s) ℹ Safe fix -396 396 | def a(): -397 397 | pass -398 398 | - 399 |+ -399 400 | async def b(): -400 401 | pass -401 402 | # end - -E30.py:408:1: E302 [*] Expected 2 blank lines, found 1 - | -406 | pass -407 | -408 | async def x(y: int = 1): +408 408 | def a(): +409 409 | pass +410 410 | + 411 |+ +411 412 | async def b(): +412 413 | pass +413 414 | # end + +E30.py:420:1: E302 [*] Expected 2 blank lines, found 1 + | +418 | pass +419 | +420 | async def x(y: int = 1): | ^^^^^ E302 -409 | pass -410 | # end +421 | pass +422 | # end | = help: Add missing blank line(s) ℹ Safe fix -405 405 | async def x(): -406 406 | pass -407 407 | - 408 |+ -408 409 | async def x(y: int = 1): -409 410 | pass -410 411 | # end - -E30.py:416:1: E302 [*] Expected 2 blank lines, found 0 - | -414 | def bar(): -415 | pass -416 | def baz(): pass +417 417 | async def x(): +418 418 | pass +419 419 | + 420 |+ +420 421 | async def x(y: int = 1): +421 422 | pass +422 423 | # end + +E30.py:428:1: E302 [*] Expected 2 blank lines, found 0 + | +426 | def bar(): +427 | pass +428 | def baz(): pass | ^^^ E302 -417 | # end +429 | # end | = help: Add missing blank line(s) ℹ Safe fix -413 413 | # E302 -414 414 | def bar(): -415 415 | pass - 416 |+ - 417 |+ -416 418 | def baz(): pass -417 419 | # end -418 420 | - -E30.py:422:1: E302 [*] Expected 2 blank lines, found 0 - | -420 | # E302 -421 | def bar(): pass -422 | def baz(): +425 425 | # E302 +426 426 | def bar(): +427 427 | pass + 428 |+ + 429 |+ +428 430 | def baz(): pass +429 431 | # end +430 432 | + +E30.py:434:1: E302 [*] Expected 2 blank lines, found 0 + | +432 | # E302 +433 | def bar(): pass +434 | def baz(): | ^^^ E302 -423 | pass -424 | # end +435 | pass +436 | # end | = help: Add missing blank line(s) ℹ Safe fix -419 419 | -420 420 | # E302 -421 421 | def bar(): pass - 422 |+ - 423 |+ -422 424 | def baz(): -423 425 | pass -424 426 | # end - -E30.py:432:1: E302 [*] Expected 2 blank lines, found 1 - | -431 | # comment -432 | @decorator +431 431 | +432 432 | # E302 +433 433 | def bar(): pass + 434 |+ + 435 |+ +434 436 | def baz(): +435 437 | pass +436 438 | # end + +E30.py:444:1: E302 [*] Expected 2 blank lines, found 1 + | +443 | # comment +444 | @decorator | ^ E302 -433 | def g(): -434 | pass +445 | def g(): +446 | pass | = help: Add missing blank line(s) ℹ Safe fix -428 428 | def f(): -429 429 | pass -430 430 | - 431 |+ -431 432 | # comment -432 433 | @decorator -433 434 | def g(): +440 440 | def f(): +441 441 | pass +442 442 | + 443 |+ +443 444 | # comment +444 445 | @decorator +445 446 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 8c44bb45d05b4..2f75dfe18fbb2 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,30 +1,12 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:443:5: E303 [*] Too many blank lines (2) - | -443 | # arbitrary comment - | ^^^^^^^^^^^^^^^^^^^ E303 -444 | -445 | def inner(): # E306 not expected - | - = help: Remove extraneous blank line(s) - -ℹ Safe fix -439 439 | def fn(): -440 440 | _ = None -441 441 | -442 |- -443 442 | # arbitrary comment -444 443 | -445 444 | def inner(): # E306 not expected - E30.py:455:5: E303 [*] Too many blank lines (2) | 455 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -456 | def inner(): # E306 not expected -457 | pass +456 | +457 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) @@ -34,95 +16,113 @@ E30.py:455:5: E303 [*] Too many blank lines (2) 453 453 | 454 |- 455 454 | # arbitrary comment -456 455 | def inner(): # E306 not expected -457 456 | pass +456 455 | +457 456 | def inner(): # E306 not expected + +E30.py:467:5: E303 [*] Too many blank lines (2) + | +467 | # arbitrary comment + | ^^^^^^^^^^^^^^^^^^^ E303 +468 | def inner(): # E306 not expected +469 | pass + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +463 463 | def fn(): +464 464 | _ = None +465 465 | +466 |- +467 466 | # arbitrary comment +468 467 | def inner(): # E306 not expected +469 468 | pass -E30.py:466:1: E303 [*] Too many blank lines (3) +E30.py:478:1: E303 [*] Too many blank lines (3) | -466 | print() +478 | print() | ^^^^^ E303 -467 | # end +479 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -462 462 | print() -463 463 | -464 464 | -465 |- -466 465 | print() -467 466 | # end -468 467 | +474 474 | print() +475 475 | +476 476 | +477 |- +478 477 | print() +479 478 | # end +480 479 | -E30.py:475:1: E303 [*] Too many blank lines (3) +E30.py:487:1: E303 [*] Too many blank lines (3) | -475 | # comment +487 | # comment | ^^^^^^^^^ E303 -476 | -477 | print() +488 | +489 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -471 471 | print() -472 472 | -473 473 | -474 |- -475 474 | # comment -476 475 | -477 476 | print() +483 483 | print() +484 484 | +485 485 | +486 |- +487 486 | # comment +488 487 | +489 488 | print() -E30.py:486:5: E303 [*] Too many blank lines (2) +E30.py:498:5: E303 [*] Too many blank lines (2) | -486 | # comment +498 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -482 482 | def a(): -483 483 | print() -484 484 | -485 |- -486 485 | # comment -487 486 | -488 487 | +494 494 | def a(): +495 495 | print() +496 496 | +497 |- +498 497 | # comment +499 498 | +500 499 | -E30.py:489:5: E303 [*] Too many blank lines (2) +E30.py:501:5: E303 [*] Too many blank lines (2) | -489 | # another comment +501 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -490 | -491 | print() +502 | +503 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -485 485 | -486 486 | # comment -487 487 | -488 |- -489 488 | # another comment -490 489 | -491 490 | print() +497 497 | +498 498 | # comment +499 499 | +500 |- +501 500 | # another comment +502 501 | +503 502 | print() -E30.py:500:1: E303 [*] Too many blank lines (3) +E30.py:512:1: E303 [*] Too many blank lines (3) | -500 | / """This class docstring comes on line 5. -501 | | It gives error E303: too many blank lines (3) -502 | | """ +512 | / """This class docstring comes on line 5. +513 | | It gives error E303: too many blank lines (3) +514 | | """ | |___^ E303 -503 | # end +515 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -496 496 | #!python -497 497 | -498 498 | -499 |- -500 499 | """This class docstring comes on line 5. -501 500 | It gives error E303: too many blank lines (3) -502 501 | """ +508 508 | #!python +509 509 | +510 510 | +511 |- +512 511 | """This class docstring comes on line 5. +513 512 | It gives error E303: too many blank lines (3) +514 513 | """ diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 3d3e73ce7e388..54b8fe7c92869 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:509:1: E304 [*] blank lines found after function decorator +E30.py:521:1: E304 [*] blank lines found after function decorator | -507 | @decorator -508 | -509 | def function(): +519 | @decorator +520 | +521 | def function(): | ^^^ E304 -510 | pass -511 | # end +522 | pass +523 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -505 505 | -506 506 | # E304 -507 507 | @decorator -508 |- -509 508 | def function(): -510 509 | pass -511 510 | # end +517 517 | +518 518 | # E304 +519 519 | @decorator +520 |- +521 520 | def function(): +522 521 | pass +523 522 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 970f602afe712..dc30b8f8b471a 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:521:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:533:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -520 | # another comment -521 | fn() +532 | # another comment +533 | fn() | ^^ E305 -522 | # end +534 | # end | = help: Add missing blank line(s) ℹ Safe fix -518 518 | # comment -519 519 | -520 520 | # another comment - 521 |+ - 522 |+ -521 523 | fn() -522 524 | # end -523 525 | +530 530 | # comment +531 531 | +532 532 | # another comment + 533 |+ + 534 |+ +533 535 | fn() +534 536 | # end +535 537 | -E30.py:532:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:544:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -531 | # another comment -532 | a = 1 +543 | # another comment +544 | a = 1 | ^ E305 -533 | # end +545 | # end | = help: Add missing blank line(s) ℹ Safe fix -529 529 | # comment -530 530 | -531 531 | # another comment - 532 |+ - 533 |+ -532 534 | a = 1 -533 535 | # end -534 536 | +541 541 | # comment +542 542 | +543 543 | # another comment + 544 |+ + 545 |+ +544 546 | a = 1 +545 547 | # end +546 548 | -E30.py:544:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:556:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -542 | # another comment -543 | -544 | try: +554 | # another comment +555 | +556 | try: | ^^^ E305 -545 | fn() -546 | except Exception: +557 | fn() +558 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -541 541 | -542 542 | # another comment -543 543 | - 544 |+ -544 545 | try: -545 546 | fn() -546 547 | except Exception: +553 553 | +554 554 | # another comment +555 555 | + 556 |+ +556 557 | try: +557 558 | fn() +558 559 | except Exception: -E30.py:556:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:568:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -555 | # Two spaces before comments, too. -556 | if a(): +567 | # Two spaces before comments, too. +568 | if a(): | ^^ E305 -557 | a() -558 | # end +569 | a() +570 | # end | = help: Add missing blank line(s) ℹ Safe fix -553 553 | print -554 554 | -555 555 | # Two spaces before comments, too. - 556 |+ - 557 |+ -556 558 | if a(): -557 559 | a() -558 560 | # end +565 565 | print +566 566 | +567 567 | # Two spaces before comments, too. + 568 |+ + 569 |+ +568 570 | if a(): +569 571 | a() +570 572 | # end -E30.py:569:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:581:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -567 | blah, blah -568 | -569 | if __name__ == '__main__': +579 | blah, blah +580 | +581 | if __name__ == '__main__': | ^^ E305 -570 | main() -571 | # end +582 | main() +583 | # end | = help: Add missing blank line(s) ℹ Safe fix -566 566 | def main(): -567 567 | blah, blah -568 568 | - 569 |+ -569 570 | if __name__ == '__main__': -570 571 | main() -571 572 | # end +578 578 | def main(): +579 579 | blah, blah +580 580 | + 581 |+ +581 582 | if __name__ == '__main__': +582 583 | main() +583 584 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index f4a1e714532bf..28eebb3cf76c2 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:577:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:589:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -575 | def a(): -576 | x = 1 -577 | def b(): +587 | def a(): +588 | x = 1 +589 | def b(): | ^^^ E306 -578 | pass -579 | # end +590 | pass +591 | # end | = help: Add missing blank line ℹ Safe fix -574 574 | # E306:3:5 -575 575 | def a(): -576 576 | x = 1 - 577 |+ -577 578 | def b(): -578 579 | pass -579 580 | # end - -E30.py:585:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -583 | async def a(): -584 | x = 1 -585 | def b(): +586 586 | # E306:3:5 +587 587 | def a(): +588 588 | x = 1 + 589 |+ +589 590 | def b(): +590 591 | pass +591 592 | # end + +E30.py:597:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +595 | async def a(): +596 | x = 1 +597 | def b(): | ^^^ E306 -586 | pass -587 | # end +598 | pass +599 | # end | = help: Add missing blank line ℹ Safe fix -582 582 | #: E306:3:5 -583 583 | async def a(): -584 584 | x = 1 - 585 |+ -585 586 | def b(): -586 587 | pass -587 588 | # end - -E30.py:593:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -591 | def a(): -592 | x = 2 -593 | def b(): +594 594 | #: E306:3:5 +595 595 | async def a(): +596 596 | x = 1 + 597 |+ +597 598 | def b(): +598 599 | pass +599 600 | # end + +E30.py:605:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +603 | def a(): +604 | x = 2 +605 | def b(): | ^^^ E306 -594 | x = 1 -595 | def c(): +606 | x = 1 +607 | def c(): | = help: Add missing blank line ℹ Safe fix -590 590 | #: E306:3:5 E306:5:9 -591 591 | def a(): -592 592 | x = 2 - 593 |+ -593 594 | def b(): -594 595 | x = 1 -595 596 | def c(): - -E30.py:595:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -593 | def b(): -594 | x = 1 -595 | def c(): +602 602 | #: E306:3:5 E306:5:9 +603 603 | def a(): +604 604 | x = 2 + 605 |+ +605 606 | def b(): +606 607 | x = 1 +607 608 | def c(): + +E30.py:607:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +605 | def b(): +606 | x = 1 +607 | def c(): | ^^^ E306 -596 | pass -597 | # end +608 | pass +609 | # end | = help: Add missing blank line ℹ Safe fix -592 592 | x = 2 -593 593 | def b(): -594 594 | x = 1 - 595 |+ -595 596 | def c(): -596 597 | pass -597 598 | # end - -E30.py:603:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -601 | def a(): -602 | x = 1 -603 | class C: +604 604 | x = 2 +605 605 | def b(): +606 606 | x = 1 + 607 |+ +607 608 | def c(): +608 609 | pass +609 610 | # end + +E30.py:615:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +613 | def a(): +614 | x = 1 +615 | class C: | ^^^^^ E306 -604 | pass -605 | x = 2 +616 | pass +617 | x = 2 | = help: Add missing blank line ℹ Safe fix -600 600 | # E306:3:5 E306:6:5 -601 601 | def a(): -602 602 | x = 1 - 603 |+ -603 604 | class C: -604 605 | pass -605 606 | x = 2 - -E30.py:606:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -604 | pass -605 | x = 2 -606 | def b(): +612 612 | # E306:3:5 E306:6:5 +613 613 | def a(): +614 614 | x = 1 + 615 |+ +615 616 | class C: +616 617 | pass +617 618 | x = 2 + +E30.py:618:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +616 | pass +617 | x = 2 +618 | def b(): | ^^^ E306 -607 | pass -608 | # end +619 | pass +620 | # end | = help: Add missing blank line ℹ Safe fix -603 603 | class C: -604 604 | pass -605 605 | x = 2 - 606 |+ -606 607 | def b(): -607 608 | pass -608 609 | # end - -E30.py:615:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -613 | def bar(): -614 | pass -615 | def baz(): pass +615 615 | class C: +616 616 | pass +617 617 | x = 2 + 618 |+ +618 619 | def b(): +619 620 | pass +620 621 | # end + +E30.py:627:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +625 | def bar(): +626 | pass +627 | def baz(): pass | ^^^ E306 -616 | # end +628 | # end | = help: Add missing blank line ℹ Safe fix -612 612 | def foo(): -613 613 | def bar(): -614 614 | pass - 615 |+ -615 616 | def baz(): pass -616 617 | # end -617 618 | - -E30.py:622:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -620 | def foo(): -621 | def bar(): pass -622 | def baz(): +624 624 | def foo(): +625 625 | def bar(): +626 626 | pass + 627 |+ +627 628 | def baz(): pass +628 629 | # end +629 630 | + +E30.py:634:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +632 | def foo(): +633 | def bar(): pass +634 | def baz(): | ^^^ E306 -623 | pass -624 | # end +635 | pass +636 | # end | = help: Add missing blank line ℹ Safe fix -619 619 | # E306:3:5 -620 620 | def foo(): -621 621 | def bar(): pass - 622 |+ -622 623 | def baz(): -623 624 | pass -624 625 | # end +631 631 | # E306:3:5 +632 632 | def foo(): +633 633 | def bar(): pass + 634 |+ +634 635 | def baz(): +635 636 | pass +636 637 | # end From 6d0fbf2e315ae4cc1ec67ac1fd2058f9c126d12e Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 19 Nov 2023 22:29:26 +0900 Subject: [PATCH 030/122] Make rules independent. This will slow down things a bit, but makes it clearer what the conditions for a given rule to trigger are. --- .../rules/logical_lines/blank_lines.rs | 307 +++++++++--------- 1 file changed, 159 insertions(+), 148 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 444617f0f4f94..b5e52bc7b9244 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -166,9 +166,9 @@ impl AlwaysFixableViolation for BlankLinesTopLevel { /// Checks for extraneous blank lines. /// /// ## Why is this bad? -/// PEP 8 recommends the using blank lines as following: -/// - Two blank lines are expected between functions and classes -/// - One blank line is expected between methods of a class. +/// PEP 8 recommends using blank lines as follows: +/// - Surround top-level function and class definitions with two blank lines. +/// - Surround method definitions inside a class by a single blank line. /// /// ## Example /// ```python @@ -424,155 +424,166 @@ pub(crate) fn blank_lines( continue; } - if tracked_vars.is_first_logical_line { - // Don't expect blank lines before the first line non comment line. - } else if token.kind() == TokenKind::Def - // Only applies to method. - && tracked_vars.is_in_class - // The class/parent method's docstring can directly precede the def. - && !tracked_vars.follows_docstring - // Do not trigger when then def follows an if/while/etc... - && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) - && ( - // A comment before the def is allowed (as long as it is preceded by a blank line). - (line.line.preceding_blank_lines == 0 && line.line.blank_lines == 0 && prev_line.is_some_and(LogicalLine::is_comment_only)) - // Standard case. - || line.line.blank_lines == 0 - && prev_line - .and_then(|prev_line| prev_line.tokens_trimmed().first()) - .map_or(false, |token| { - !matches!( - token.kind(), - TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async - ) - }) - ) - { - // E301 - let mut diagnostic = - Diagnostic::new(BlankLineBetweenMethods(line.line.blank_lines), token.range); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), - locator.line_start(tracked_vars.last_non_comment_line_end), - ))); - context.push_diagnostic(diagnostic); - } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async) - && !( - // Allow decorators. - tracked_vars.follows_decorator - // Allow groups of one-liners. - || (tracked_vars.follows_def - && line - .tokens_trimmed() - .last() - .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + // Don't expect blank lines before the first line non comment line. + if !tracked_vars.is_first_logical_line { + if token.kind() == TokenKind::Def + // Only applies to methods. + && tracked_vars.is_in_class + // The class/parent method's docstring can directly precede the def. + && !tracked_vars.follows_docstring + // Do not trigger when then def follows an if/while/etc... + && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) + && ( + // A comment before the def is allowed (as long as it is preceded by a blank line). + (line.line.preceding_blank_lines == 0 && line.line.blank_lines == 0 && prev_line.is_some_and(LogicalLine::is_comment_only)) + // Standard case. + || line.line.blank_lines == 0 + && prev_line + .and_then(|prev_line| prev_line.tokens_trimmed().first()) + .map_or(false, |token| { + !matches!( + token.kind(), + TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async + ) + }) ) - // Only apply to top level functions. - || tracked_vars.is_in_class - || tracked_vars.is_in_fn - // If a comment is preceding the def/class, the 2 blank lines can be before that comment. - || (line.line.preceding_blank_lines >= BlankLinesConfig::TOP_LEVEL && prev_line.map_or(false, LogicalLine::is_comment_only)) - ) - // Only trigger on non-indented classes and functions (for example functions within an if are ignored) - && indent_level == 0 - // Allow directly following an except. - && prev_line - .and_then(|prev_line| prev_line.tokens_trimmed().first()) - .map_or(true, |token| !matches!(token.kind(), TokenKind::Except)) - && line.line.blank_lines < BlankLinesConfig::TOP_LEVEL - { - // E302 - let mut diagnostic = Diagnostic::new( - BlankLinesTopLevel(line.line.preceding_blank_lines), - token.range, - ); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string().repeat( - (BlankLinesConfig::TOP_LEVEL - line.line.preceding_blank_lines) as usize, - ), - locator.line_start(tracked_vars.last_non_comment_line_end), - ))); - - context.push_diagnostic(diagnostic); - } else if line.line.blank_lines > BlankLinesConfig::TOP_LEVEL - || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) - && line.line.blank_lines > BlankLinesConfig::METHOD) - { - // E303 - let mut diagnostic = - Diagnostic::new(TooManyBlankLines(line.line.blank_lines), token.range); - - let chars_to_remove = if indent_level > 0 { - line.line.preceding_blank_characters - BlankLinesConfig::METHOD - } else { - line.line.preceding_blank_characters - BlankLinesConfig::TOP_LEVEL - }; - let end = locator.line_start(token.range.start()); - let start = end - TextSize::new(chars_to_remove); - diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); - - context.push_diagnostic(diagnostic); - } else if tracked_vars.follows_decorator && line.line.blank_lines > 0 { - // E304 - let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, token.range); - - let range = token.range; - diagnostic.set_fix(Fix::safe_edit(Edit::deletion( - locator.line_start(range.start()) - - TextSize::new(line.line.preceding_blank_characters), - locator.line_start(range.start()), - ))); - - context.push_diagnostic(diagnostic); - } else if line.line.preceding_blank_lines < BlankLinesConfig::TOP_LEVEL - && is_top_level_token(tracked_vars.previous_unindented_token) - && indent_level == 0 - && !line_is_comment_only - && !is_top_level_token_or_decorator(Some(token.kind)) - { - // E305 - let mut diagnostic = Diagnostic::new( - BlankLinesAfterFunctionOrClass(line.line.blank_lines), - token.range, - ); + { + // E301 + let mut diagnostic = + Diagnostic::new(BlankLineBetweenMethods(line.line.blank_lines), token.range); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(tracked_vars.last_non_comment_line_end), + ))); + context.push_diagnostic(diagnostic); + } - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .as_str() - .to_string() - .repeat((BlankLinesConfig::TOP_LEVEL - line.line.blank_lines) as usize), - locator.line_start(token.range.start()), - ))); - - context.push_diagnostic(diagnostic); - } else if matches!(token.kind(), TokenKind::Def | TokenKind::Class) - && (tracked_vars.is_in_class || tracked_vars.is_in_fn) - && line.line.preceding_blank_lines == 0 - && !is_decorator(prev_line) - // The class's docstring can directly precede the first function. - && !tracked_vars.follows_docstring - && !prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level < indent_level) - // Allow groups of one-liners. - && !(tracked_vars.follows_def - && line - .tokens_trimmed() - .last() - .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) - ) - { - // E306 - let mut diagnostic = Diagnostic::new( - BlankLinesBeforeNestedDefinition(line.line.blank_lines), - token.range, - ); + if is_top_level_token_or_decorator(Some(token.kind)) + && !( + // Allow decorators. + tracked_vars.follows_decorator + // Allow groups of one-liners. + || (tracked_vars.follows_def + && line + .tokens_trimmed() + .last() + .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + ) + // Only apply to top level functions. + || tracked_vars.is_in_class + || tracked_vars.is_in_fn + // If a comment is preceding the def/class, the 2 blank lines can be before that comment. + || (line.line.preceding_blank_lines >= BlankLinesConfig::TOP_LEVEL && prev_line.map_or(false, LogicalLine::is_comment_only)) + ) + // Only trigger on non-indented classes and functions (for example functions within an if are ignored) + && indent_level == 0 + // Allow directly following an except. + && prev_line + .and_then(|prev_line| prev_line.tokens_trimmed().first()) + .map_or(true, |token| !matches!(token.kind(), TokenKind::Except)) + && line.line.blank_lines < BlankLinesConfig::TOP_LEVEL + { + // E302 + let mut diagnostic = Diagnostic::new( + BlankLinesTopLevel(line.line.preceding_blank_lines), + token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string().repeat( + (BlankLinesConfig::TOP_LEVEL - line.line.preceding_blank_lines) as usize, + ), + locator.line_start(tracked_vars.last_non_comment_line_end), + ))); + + context.push_diagnostic(diagnostic); + } + + if line.line.blank_lines > BlankLinesConfig::TOP_LEVEL + || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) + && line.line.blank_lines > BlankLinesConfig::METHOD) + { + // E303 + let mut diagnostic = + Diagnostic::new(TooManyBlankLines(line.line.blank_lines), token.range); + + let chars_to_remove = if indent_level > 0 { + line.line.preceding_blank_characters - BlankLinesConfig::METHOD + } else { + line.line.preceding_blank_characters - BlankLinesConfig::TOP_LEVEL + }; + let end = locator.line_start(token.range.start()); + let start = end - TextSize::new(chars_to_remove); + diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); + + context.push_diagnostic(diagnostic); + } - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), - locator.line_start(token.range.start()), - ))); + if tracked_vars.follows_decorator && line.line.preceding_blank_lines > 0 { + // E304 + let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, token.range); + + let range = token.range; + diagnostic.set_fix(Fix::safe_edit(Edit::deletion( + locator.line_start(range.start()) + - TextSize::new(line.line.preceding_blank_characters), + locator.line_start(range.start()), + ))); + + context.push_diagnostic(diagnostic); + } - context.push_diagnostic(diagnostic); + if line.line.preceding_blank_lines < BlankLinesConfig::TOP_LEVEL + && is_top_level_token(tracked_vars.previous_unindented_token) + && indent_level == 0 + && !line_is_comment_only + && !is_top_level_token_or_decorator(Some(token.kind)) + { + // E305 + let mut diagnostic = Diagnostic::new( + BlankLinesAfterFunctionOrClass(line.line.blank_lines), + token.range, + ); + + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .as_str() + .to_string() + .repeat((BlankLinesConfig::TOP_LEVEL - line.line.blank_lines) as usize), + locator.line_start(token.range.start()), + ))); + + context.push_diagnostic(diagnostic); + } + + if matches!(token.kind(), TokenKind::Def | TokenKind::Class) + && tracked_vars.is_in_fn + && line.line.preceding_blank_lines == 0 + && !is_decorator(prev_line) + // The class's docstring can directly precede the first function. + && !tracked_vars.follows_docstring + && !prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level < indent_level) + // Allow groups of one-liners. + && !(tracked_vars.follows_def + && line + .tokens_trimmed() + .last() + .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + ) + { + // E306 + let mut diagnostic = Diagnostic::new( + BlankLinesBeforeNestedDefinition(line.line.blank_lines), + token.range, + ); + + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(token.range.start()), + ))); + + context.push_diagnostic(diagnostic); + } } match token.kind() { From f7b2f8937f391cee802faa22556426270fe18aba Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 19 Nov 2023 22:36:49 +0900 Subject: [PATCH 031/122] Fix typo. --- .../src/rules/pycodestyle/rules/logical_lines/blank_lines.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index b5e52bc7b9244..cd6445d9d6c21 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -424,14 +424,14 @@ pub(crate) fn blank_lines( continue; } - // Don't expect blank lines before the first line non comment line. + // Don't expect blank lines before the first non comment line. if !tracked_vars.is_first_logical_line { if token.kind() == TokenKind::Def // Only applies to methods. && tracked_vars.is_in_class // The class/parent method's docstring can directly precede the def. && !tracked_vars.follows_docstring - // Do not trigger when then def follows an if/while/etc... + // Do not trigger when the def follows an if/while/etc... && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) && ( // A comment before the def is allowed (as long as it is preceded by a blank line). From 53658304ae337568a9db35715d4cb829050df5ea Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Mon, 20 Nov 2023 00:16:35 +0900 Subject: [PATCH 032/122] Rule simplification. Also fix a E306 regression. --- .../test/fixtures/pycodestyle/E30.py | 9 +-- .../rules/logical_lines/blank_lines.rs | 58 ++++++++----------- ...ules__pycodestyle__tests__E306_E30.py.snap | 40 +++++++++++++ 3 files changed, 66 insertions(+), 41 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 5cc5362d6cd52..dd9914a959315 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -620,7 +620,7 @@ def b(): # end -# E306:4:5 +# E306 def foo(): def bar(): pass @@ -636,13 +636,6 @@ def baz(): # end -# E306 -class C: - def f(): - pass -# end - - # E306 def f(): def f(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index cd6445d9d6c21..637354f245b52 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -365,13 +365,11 @@ fn is_top_level_token(token: Option) -> bool { } /// Returns `true` if the token is At, Async, Class or Def -fn is_top_level_token_or_decorator(token: Option) -> bool { - token.is_some_and(|token| { - matches!( - token, - TokenKind::Class | TokenKind::Def | TokenKind::Async | TokenKind::At - ) - }) +fn is_top_level_token_or_decorator(token: TokenKind) -> bool { + matches!( + token, + TokenKind::Class | TokenKind::Def | TokenKind::Async | TokenKind::At + ) } /// E301, E302, E303, E304, E305, E306 @@ -426,8 +424,9 @@ pub(crate) fn blank_lines( // Don't expect blank lines before the first non comment line. if !tracked_vars.is_first_logical_line { - if token.kind() == TokenKind::Def + if line.line.preceding_blank_lines == 0 // Only applies to methods. + && token.kind() == TokenKind::Def && tracked_vars.is_in_class // The class/parent method's docstring can directly precede the def. && !tracked_vars.follows_docstring @@ -435,32 +434,29 @@ pub(crate) fn blank_lines( && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) && ( // A comment before the def is allowed (as long as it is preceded by a blank line). - (line.line.preceding_blank_lines == 0 && line.line.blank_lines == 0 && prev_line.is_some_and(LogicalLine::is_comment_only)) + prev_line.is_some_and(LogicalLine::is_comment_only) // Standard case. - || line.line.blank_lines == 0 - && prev_line + || prev_line .and_then(|prev_line| prev_line.tokens_trimmed().first()) - .map_or(false, |token| { - !matches!( - token.kind(), - TokenKind::Def | TokenKind::Class | TokenKind::At | TokenKind::Async - ) - }) + .is_some_and(|token| !is_top_level_token_or_decorator(token.kind)) ) { // E301 - let mut diagnostic = - Diagnostic::new(BlankLineBetweenMethods(line.line.blank_lines), token.range); + let mut diagnostic = Diagnostic::new( + BlankLineBetweenMethods(line.line.preceding_blank_lines), + token.range, + ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().as_str().to_string(), locator.line_start(tracked_vars.last_non_comment_line_end), ))); + context.push_diagnostic(diagnostic); } - if is_top_level_token_or_decorator(Some(token.kind)) + if line.line.blank_lines < BlankLinesConfig::TOP_LEVEL && !( - // Allow decorators. + // Allow decorators (if there is an error it will be triggered on the decorator). tracked_vars.follows_decorator // Allow groups of one-liners. || (tracked_vars.follows_def @@ -469,19 +465,13 @@ pub(crate) fn blank_lines( .last() .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) ) - // Only apply to top level functions. - || tracked_vars.is_in_class - || tracked_vars.is_in_fn // If a comment is preceding the def/class, the 2 blank lines can be before that comment. || (line.line.preceding_blank_lines >= BlankLinesConfig::TOP_LEVEL && prev_line.map_or(false, LogicalLine::is_comment_only)) ) // Only trigger on non-indented classes and functions (for example functions within an if are ignored) && indent_level == 0 - // Allow directly following an except. - && prev_line - .and_then(|prev_line| prev_line.tokens_trimmed().first()) - .map_or(true, |token| !matches!(token.kind(), TokenKind::Except)) - && line.line.blank_lines < BlankLinesConfig::TOP_LEVEL + // Only apply to functions or classes. + && is_top_level_token_or_decorator(token.kind) { // E302 let mut diagnostic = Diagnostic::new( @@ -536,7 +526,7 @@ pub(crate) fn blank_lines( && is_top_level_token(tracked_vars.previous_unindented_token) && indent_level == 0 && !line_is_comment_only - && !is_top_level_token_or_decorator(Some(token.kind)) + && !is_top_level_token_or_decorator(token.kind) { // E305 let mut diagnostic = Diagnostic::new( @@ -556,13 +546,15 @@ pub(crate) fn blank_lines( context.push_diagnostic(diagnostic); } - if matches!(token.kind(), TokenKind::Def | TokenKind::Class) + if line.line.preceding_blank_lines == 0 + // Only apply to nested functions. && tracked_vars.is_in_fn - && line.line.preceding_blank_lines == 0 + && matches!(token.kind(), TokenKind::Def | TokenKind::Class) && !is_decorator(prev_line) // The class's docstring can directly precede the first function. && !tracked_vars.follows_docstring - && !prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level < indent_level) + // Do not trigger when the def/class follows an "indenting token" (if/while/etc...), unless that "indenting token" is a def. + && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level || tracked_vars.follows_def) // Allow groups of one-liners. && !(tracked_vars.follows_def && line diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 28eebb3cf76c2..f15b932b133fd 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -121,6 +121,26 @@ E30.py:618:5: E306 [*] Expected 1 blank line before a nested definition, found 0 619 620 | pass 620 621 | # end +E30.py:625:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +623 | # E306:4:5 +624 | def foo(): +625 | def bar(): + | ^^^ E306 +626 | pass +627 | def baz(): pass + | + = help: Add missing blank line + +ℹ Safe fix +622 622 | +623 623 | # E306:4:5 +624 624 | def foo(): + 625 |+ +625 626 | def bar(): +626 627 | pass +627 628 | def baz(): pass + E30.py:627:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | 625 | def bar(): @@ -160,4 +180,24 @@ E30.py:634:5: E306 [*] Expected 1 blank line before a nested definition, found 0 635 636 | pass 636 637 | # end +E30.py:641:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +639 | # E306 +640 | def f(): +641 | def f(): + | ^^^ E306 +642 | pass +643 | # end + | + = help: Add missing blank line + +ℹ Safe fix +638 638 | +639 639 | # E306 +640 640 | def f(): + 641 |+ +641 642 | def f(): +642 643 | pass +643 644 | # end + From bafddf94a2811c3307a69186899df25840202e13 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Mon, 20 Nov 2023 00:28:04 +0900 Subject: [PATCH 033/122] Fix E306 false negative when using async or decorator. --- .../test/fixtures/pycodestyle/E30.py | 26 ++++++++ .../rules/logical_lines/blank_lines.rs | 12 +--- ...ules__pycodestyle__tests__E306_E30.py.snap | 64 ++++++++++++++++++- 3 files changed, 91 insertions(+), 11 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index dd9914a959315..cb2545982b23e 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -641,3 +641,29 @@ def f(): def f(): pass # end + + +# E306 +def a(): + x = 2 + @decorator + def b(): + pass +# end + + +# E306 +def a(): + x = 2 + @decorator + async def b(): + pass +# end + + +# E306 +def a(): + x = 2 + async def b(): + pass +# end diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 637354f245b52..15f65371aa4c0 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -341,13 +341,6 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { } } -/// Check if the given line starts with a decorator. -fn is_decorator(line: Option<&LogicalLine>) -> bool { - line.and_then(|line| line.tokens_trimmed().first()) - .map_or(false, |token| matches!(token.kind(), TokenKind::At)) -} - -/// Check if the given line starts with a decorator. /// Returns `true` if line is a docstring only line. fn is_docstring(line: Option<&LogicalLine>) -> bool { line.is_some_and(|line| { @@ -549,8 +542,9 @@ pub(crate) fn blank_lines( if line.line.preceding_blank_lines == 0 // Only apply to nested functions. && tracked_vars.is_in_fn - && matches!(token.kind(), TokenKind::Def | TokenKind::Class) - && !is_decorator(prev_line) + && is_top_level_token_or_decorator(token.kind) + // Allow decorators (if there is an error it will be triggered on the decorator). + && !tracked_vars.follows_decorator // The class's docstring can directly precede the first function. && !tracked_vars.follows_docstring // Do not trigger when the def/class follows an "indenting token" (if/while/etc...), unless that "indenting token" is a def. diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index f15b932b133fd..1e2b4cfa1ac8e 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -123,7 +123,7 @@ E30.py:618:5: E306 [*] Expected 1 blank line before a nested definition, found 0 E30.py:625:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -623 | # E306:4:5 +623 | # E306 624 | def foo(): 625 | def bar(): | ^^^ E306 @@ -134,7 +134,7 @@ E30.py:625:5: E306 [*] Expected 1 blank line before a nested definition, found 0 ℹ Safe fix 622 622 | -623 623 | # E306:4:5 +623 623 | # E306 624 624 | def foo(): 625 |+ 625 626 | def bar(): @@ -200,4 +200,64 @@ E30.py:641:5: E306 [*] Expected 1 blank line before a nested definition, found 0 642 643 | pass 643 644 | # end +E30.py:649:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +647 | def a(): +648 | x = 2 +649 | @decorator + | ^ E306 +650 | def b(): +651 | pass + | + = help: Add missing blank line + +ℹ Safe fix +646 646 | # E306 +647 647 | def a(): +648 648 | x = 2 + 649 |+ +649 650 | @decorator +650 651 | def b(): +651 652 | pass + +E30.py:658:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +656 | def a(): +657 | x = 2 +658 | @decorator + | ^ E306 +659 | async def b(): +660 | pass + | + = help: Add missing blank line + +ℹ Safe fix +655 655 | # E306 +656 656 | def a(): +657 657 | x = 2 + 658 |+ +658 659 | @decorator +659 660 | async def b(): +660 661 | pass + +E30.py:667:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +665 | def a(): +666 | x = 2 +667 | async def b(): + | ^^^^^ E306 +668 | pass +669 | # end + | + = help: Add missing blank line + +ℹ Safe fix +664 664 | # E306 +665 665 | def a(): +666 666 | x = 2 + 667 |+ +667 668 | async def b(): +668 669 | pass +669 670 | # end + From ea9d9c83435e1c15ef7b50ffab17f0616c07b428 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Mon, 20 Nov 2023 00:47:58 +0900 Subject: [PATCH 034/122] Simplify/fix E301's condition. --- .../pycodestyle/rules/logical_lines/blank_lines.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 15f65371aa4c0..ce89aa1508dd5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -425,14 +425,8 @@ pub(crate) fn blank_lines( && !tracked_vars.follows_docstring // Do not trigger when the def follows an if/while/etc... && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) - && ( - // A comment before the def is allowed (as long as it is preceded by a blank line). - prev_line.is_some_and(LogicalLine::is_comment_only) - // Standard case. - || prev_line - .and_then(|prev_line| prev_line.tokens_trimmed().first()) - .is_some_and(|token| !is_top_level_token_or_decorator(token.kind)) - ) + // Allow following a decorator (if there is an error it will be triggered on the first decorator). + && !tracked_vars.follows_decorator { // E301 let mut diagnostic = Diagnostic::new( @@ -449,7 +443,7 @@ pub(crate) fn blank_lines( if line.line.blank_lines < BlankLinesConfig::TOP_LEVEL && !( - // Allow decorators (if there is an error it will be triggered on the decorator). + // Allow decorators (if there is an error it will be triggered on the first decorator). tracked_vars.follows_decorator // Allow groups of one-liners. || (tracked_vars.follows_def @@ -543,7 +537,7 @@ pub(crate) fn blank_lines( // Only apply to nested functions. && tracked_vars.is_in_fn && is_top_level_token_or_decorator(token.kind) - // Allow decorators (if there is an error it will be triggered on the decorator). + // Allow decorators (if there is an error it will be triggered on the first decorator). && !tracked_vars.follows_decorator // The class's docstring can directly precede the first function. && !tracked_vars.follows_docstring From 4d2f7b5b5e0ce0d3e56d1fd740eec5f05d27d698 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Mon, 20 Nov 2023 00:54:54 +0900 Subject: [PATCH 035/122] Make if condition more readable. --- .../rules/logical_lines/blank_lines.rs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index ce89aa1508dd5..14cbe9af7dee5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -442,19 +442,17 @@ pub(crate) fn blank_lines( } if line.line.blank_lines < BlankLinesConfig::TOP_LEVEL - && !( - // Allow decorators (if there is an error it will be triggered on the first decorator). - tracked_vars.follows_decorator - // Allow groups of one-liners. - || (tracked_vars.follows_def - && line - .tokens_trimmed() - .last() - .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) - ) - // If a comment is preceding the def/class, the 2 blank lines can be before that comment. - || (line.line.preceding_blank_lines >= BlankLinesConfig::TOP_LEVEL && prev_line.map_or(false, LogicalLine::is_comment_only)) + // Allow following a decorator (if there is an error it will be triggered on the first decorator). + && !tracked_vars.follows_decorator + // Allow groups of one-liners. + && !(tracked_vars.follows_def + && line + .tokens_trimmed() + .last() + .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) ) + // If a comment is preceding the def/class, the 2 blank lines can be before that comment. + && !(line.line.preceding_blank_lines >= BlankLinesConfig::TOP_LEVEL && prev_line.map_or(false, LogicalLine::is_comment_only)) // Only trigger on non-indented classes and functions (for example functions within an if are ignored) && indent_level == 0 // Only apply to functions or classes. @@ -537,7 +535,7 @@ pub(crate) fn blank_lines( // Only apply to nested functions. && tracked_vars.is_in_fn && is_top_level_token_or_decorator(token.kind) - // Allow decorators (if there is an error it will be triggered on the first decorator). + // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !tracked_vars.follows_decorator // The class's docstring can directly precede the first function. && !tracked_vars.follows_docstring From 0867011ae1c8513ab7a2c15167f9b9c927008f14 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Mon, 20 Nov 2023 01:17:32 +0900 Subject: [PATCH 036/122] Simplify E301's if condition. --- crates/ruff_linter/src/checkers/logical_lines.rs | 3 --- .../src/rules/pycodestyle/rules/logical_lines/blank_lines.rs | 5 +---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 953b1b4e9ebdf..727bc20adf072 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -41,7 +41,6 @@ pub(crate) fn check_logical_lines( let mut blank_lines_tracking_vars = BlankLinesTrackingVars::default(); let mut non_comment_prev_line = None; - let mut prev_line = None; let mut prev_indent_level = None; let indent_char = stylist.indentation().as_char(); @@ -106,7 +105,6 @@ pub(crate) fn check_logical_lines( blank_lines( &line, - prev_line.as_ref(), &mut blank_lines_tracking_vars, prev_indent_level, indent_level, @@ -120,7 +118,6 @@ pub(crate) fn check_logical_lines( non_comment_prev_line = Some(line.clone()); prev_indent_level = Some(indent_level); } - prev_line = Some(line); } context.diagnostics } diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 14cbe9af7dee5..d9c794a42d7f6 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -369,7 +369,6 @@ fn is_top_level_token_or_decorator(token: TokenKind) -> bool { #[allow(clippy::too_many_arguments)] pub(crate) fn blank_lines( line: &LogicalLine, - prev_line: Option<&LogicalLine>, tracked_vars: &mut BlankLinesTrackingVars, prev_indent_level: Option, indent_level: usize, @@ -441,7 +440,7 @@ pub(crate) fn blank_lines( context.push_diagnostic(diagnostic); } - if line.line.blank_lines < BlankLinesConfig::TOP_LEVEL + if line.line.preceding_blank_lines < BlankLinesConfig::TOP_LEVEL // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !tracked_vars.follows_decorator // Allow groups of one-liners. @@ -451,8 +450,6 @@ pub(crate) fn blank_lines( .last() .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) ) - // If a comment is preceding the def/class, the 2 blank lines can be before that comment. - && !(line.line.preceding_blank_lines >= BlankLinesConfig::TOP_LEVEL && prev_line.map_or(false, LogicalLine::is_comment_only)) // Only trigger on non-indented classes and functions (for example functions within an if are ignored) && indent_level == 0 // Only apply to functions or classes. From 9f2d339faa50d7909d9b3ceb0051c9e14f4a1ffc Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Mon, 20 Nov 2023 01:41:48 +0900 Subject: [PATCH 037/122] Remove unnecessary clone. --- crates/ruff_linter/src/checkers/logical_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 727bc20adf072..683a6ce7b693c 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -115,7 +115,7 @@ pub(crate) fn check_logical_lines( ); if !line.is_comment_only() { - non_comment_prev_line = Some(line.clone()); + non_comment_prev_line = Some(line); prev_indent_level = Some(indent_level); } } From 3eb5c62743411519e265cc57955580b6e01755f3 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 21 Nov 2023 20:17:09 +0900 Subject: [PATCH 038/122] Modify E303 to use top-levelness instead of class/def. --- .../test/fixtures/pycodestyle/E30.py | 20 ++ .../rules/logical_lines/blank_lines.rs | 7 +- ...ules__pycodestyle__tests__E303_E30.py.snap | 35 ++ ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 336 +++++++++--------- 6 files changed, 302 insertions(+), 248 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index cb2545982b23e..3439d9982389c 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -515,6 +515,26 @@ def a(): # end +# E303 +class Class: + def a(self): + pass + + + def b(self): + pass +# end + + +# E303 +if True: + a = 1 + + + a = 2 +# end + + # E304 @decorator diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index d9c794a42d7f6..c9db7a8d4961c 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -167,8 +167,8 @@ impl AlwaysFixableViolation for BlankLinesTopLevel { /// /// ## Why is this bad? /// PEP 8 recommends using blank lines as follows: -/// - Surround top-level function and class definitions with two blank lines. -/// - Surround method definitions inside a class by a single blank line. +/// - No more than two blank lines between top-level statements. +/// - No more than one blank line between non-top-level statements. /// /// ## Example /// ```python @@ -471,8 +471,7 @@ pub(crate) fn blank_lines( } if line.line.blank_lines > BlankLinesConfig::TOP_LEVEL - || ((tracked_vars.is_in_class || tracked_vars.is_in_fn) - && line.line.blank_lines > BlankLinesConfig::METHOD) + || (indent_level > 0 && line.line.blank_lines > BlankLinesConfig::METHOD) { // E303 let mut diagnostic = diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 2f75dfe18fbb2..29c3ce985c118 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -125,4 +125,39 @@ E30.py:512:1: E303 [*] Too many blank lines (3) 513 512 | It gives error E303: too many blank lines (3) 514 513 | """ +E30.py:524:5: E303 [*] Too many blank lines (2) + | +524 | def b(self): + | ^^^ E303 +525 | pass +526 | # end + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +520 520 | def a(self): +521 521 | pass +522 522 | +523 |- +524 523 | def b(self): +525 524 | pass +526 525 | # end + +E30.py:534:5: E303 [*] Too many blank lines (2) + | +534 | a = 2 + | ^ E303 +535 | # end + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +530 530 | if True: +531 531 | a = 1 +532 532 | +533 |- +534 533 | a = 2 +535 534 | # end +536 535 | + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 54b8fe7c92869..b65d50e537bcb 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:521:1: E304 [*] blank lines found after function decorator +E30.py:541:1: E304 [*] blank lines found after function decorator | -519 | @decorator -520 | -521 | def function(): +539 | @decorator +540 | +541 | def function(): | ^^^ E304 -522 | pass -523 | # end +542 | pass +543 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -517 517 | -518 518 | # E304 -519 519 | @decorator -520 |- -521 520 | def function(): -522 521 | pass -523 522 | # end +537 537 | +538 538 | # E304 +539 539 | @decorator +540 |- +541 540 | def function(): +542 541 | pass +543 542 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index dc30b8f8b471a..87e987f761575 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:533:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:553:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -532 | # another comment -533 | fn() +552 | # another comment +553 | fn() | ^^ E305 -534 | # end +554 | # end | = help: Add missing blank line(s) ℹ Safe fix -530 530 | # comment -531 531 | -532 532 | # another comment - 533 |+ - 534 |+ -533 535 | fn() -534 536 | # end -535 537 | +550 550 | # comment +551 551 | +552 552 | # another comment + 553 |+ + 554 |+ +553 555 | fn() +554 556 | # end +555 557 | -E30.py:544:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:564:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -543 | # another comment -544 | a = 1 +563 | # another comment +564 | a = 1 | ^ E305 -545 | # end +565 | # end | = help: Add missing blank line(s) ℹ Safe fix -541 541 | # comment -542 542 | -543 543 | # another comment - 544 |+ - 545 |+ -544 546 | a = 1 -545 547 | # end -546 548 | +561 561 | # comment +562 562 | +563 563 | # another comment + 564 |+ + 565 |+ +564 566 | a = 1 +565 567 | # end +566 568 | -E30.py:556:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:576:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -554 | # another comment -555 | -556 | try: +574 | # another comment +575 | +576 | try: | ^^^ E305 -557 | fn() -558 | except Exception: +577 | fn() +578 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -553 553 | -554 554 | # another comment -555 555 | - 556 |+ -556 557 | try: -557 558 | fn() -558 559 | except Exception: +573 573 | +574 574 | # another comment +575 575 | + 576 |+ +576 577 | try: +577 578 | fn() +578 579 | except Exception: -E30.py:568:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:588:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -567 | # Two spaces before comments, too. -568 | if a(): +587 | # Two spaces before comments, too. +588 | if a(): | ^^ E305 -569 | a() -570 | # end +589 | a() +590 | # end | = help: Add missing blank line(s) ℹ Safe fix -565 565 | print -566 566 | -567 567 | # Two spaces before comments, too. - 568 |+ - 569 |+ -568 570 | if a(): -569 571 | a() -570 572 | # end +585 585 | print +586 586 | +587 587 | # Two spaces before comments, too. + 588 |+ + 589 |+ +588 590 | if a(): +589 591 | a() +590 592 | # end -E30.py:581:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:601:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -579 | blah, blah -580 | -581 | if __name__ == '__main__': +599 | blah, blah +600 | +601 | if __name__ == '__main__': | ^^ E305 -582 | main() -583 | # end +602 | main() +603 | # end | = help: Add missing blank line(s) ℹ Safe fix -578 578 | def main(): -579 579 | blah, blah -580 580 | - 581 |+ -581 582 | if __name__ == '__main__': -582 583 | main() -583 584 | # end +598 598 | def main(): +599 599 | blah, blah +600 600 | + 601 |+ +601 602 | if __name__ == '__main__': +602 603 | main() +603 604 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 1e2b4cfa1ac8e..d045a07886b18 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,263 +1,263 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:589:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:609:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -587 | def a(): -588 | x = 1 -589 | def b(): +607 | def a(): +608 | x = 1 +609 | def b(): | ^^^ E306 -590 | pass -591 | # end +610 | pass +611 | # end | = help: Add missing blank line ℹ Safe fix -586 586 | # E306:3:5 -587 587 | def a(): -588 588 | x = 1 - 589 |+ -589 590 | def b(): -590 591 | pass -591 592 | # end +606 606 | # E306:3:5 +607 607 | def a(): +608 608 | x = 1 + 609 |+ +609 610 | def b(): +610 611 | pass +611 612 | # end -E30.py:597:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:617:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -595 | async def a(): -596 | x = 1 -597 | def b(): +615 | async def a(): +616 | x = 1 +617 | def b(): | ^^^ E306 -598 | pass -599 | # end +618 | pass +619 | # end | = help: Add missing blank line ℹ Safe fix -594 594 | #: E306:3:5 -595 595 | async def a(): -596 596 | x = 1 - 597 |+ -597 598 | def b(): -598 599 | pass -599 600 | # end +614 614 | #: E306:3:5 +615 615 | async def a(): +616 616 | x = 1 + 617 |+ +617 618 | def b(): +618 619 | pass +619 620 | # end -E30.py:605:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:625:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -603 | def a(): -604 | x = 2 -605 | def b(): +623 | def a(): +624 | x = 2 +625 | def b(): | ^^^ E306 -606 | x = 1 -607 | def c(): +626 | x = 1 +627 | def c(): | = help: Add missing blank line ℹ Safe fix -602 602 | #: E306:3:5 E306:5:9 -603 603 | def a(): -604 604 | x = 2 - 605 |+ -605 606 | def b(): -606 607 | x = 1 -607 608 | def c(): +622 622 | #: E306:3:5 E306:5:9 +623 623 | def a(): +624 624 | x = 2 + 625 |+ +625 626 | def b(): +626 627 | x = 1 +627 628 | def c(): -E30.py:607:9: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:627:9: E306 [*] Expected 1 blank line before a nested definition, found 0 | -605 | def b(): -606 | x = 1 -607 | def c(): +625 | def b(): +626 | x = 1 +627 | def c(): | ^^^ E306 -608 | pass -609 | # end +628 | pass +629 | # end | = help: Add missing blank line ℹ Safe fix -604 604 | x = 2 -605 605 | def b(): -606 606 | x = 1 - 607 |+ -607 608 | def c(): -608 609 | pass -609 610 | # end +624 624 | x = 2 +625 625 | def b(): +626 626 | x = 1 + 627 |+ +627 628 | def c(): +628 629 | pass +629 630 | # end -E30.py:615:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:635:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -613 | def a(): -614 | x = 1 -615 | class C: +633 | def a(): +634 | x = 1 +635 | class C: | ^^^^^ E306 -616 | pass -617 | x = 2 +636 | pass +637 | x = 2 | = help: Add missing blank line ℹ Safe fix -612 612 | # E306:3:5 E306:6:5 -613 613 | def a(): -614 614 | x = 1 - 615 |+ -615 616 | class C: -616 617 | pass -617 618 | x = 2 +632 632 | # E306:3:5 E306:6:5 +633 633 | def a(): +634 634 | x = 1 + 635 |+ +635 636 | class C: +636 637 | pass +637 638 | x = 2 -E30.py:618:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:638:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -616 | pass -617 | x = 2 -618 | def b(): +636 | pass +637 | x = 2 +638 | def b(): | ^^^ E306 -619 | pass -620 | # end +639 | pass +640 | # end | = help: Add missing blank line ℹ Safe fix -615 615 | class C: -616 616 | pass -617 617 | x = 2 - 618 |+ -618 619 | def b(): -619 620 | pass -620 621 | # end +635 635 | class C: +636 636 | pass +637 637 | x = 2 + 638 |+ +638 639 | def b(): +639 640 | pass +640 641 | # end -E30.py:625:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:645:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -623 | # E306 -624 | def foo(): -625 | def bar(): +643 | # E306 +644 | def foo(): +645 | def bar(): | ^^^ E306 -626 | pass -627 | def baz(): pass +646 | pass +647 | def baz(): pass | = help: Add missing blank line ℹ Safe fix -622 622 | -623 623 | # E306 -624 624 | def foo(): - 625 |+ -625 626 | def bar(): -626 627 | pass -627 628 | def baz(): pass +642 642 | +643 643 | # E306 +644 644 | def foo(): + 645 |+ +645 646 | def bar(): +646 647 | pass +647 648 | def baz(): pass -E30.py:627:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:647:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -625 | def bar(): -626 | pass -627 | def baz(): pass +645 | def bar(): +646 | pass +647 | def baz(): pass | ^^^ E306 -628 | # end +648 | # end | = help: Add missing blank line ℹ Safe fix -624 624 | def foo(): -625 625 | def bar(): -626 626 | pass - 627 |+ -627 628 | def baz(): pass -628 629 | # end -629 630 | +644 644 | def foo(): +645 645 | def bar(): +646 646 | pass + 647 |+ +647 648 | def baz(): pass +648 649 | # end +649 650 | -E30.py:634:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:654:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -632 | def foo(): -633 | def bar(): pass -634 | def baz(): +652 | def foo(): +653 | def bar(): pass +654 | def baz(): | ^^^ E306 -635 | pass -636 | # end +655 | pass +656 | # end | = help: Add missing blank line ℹ Safe fix -631 631 | # E306:3:5 -632 632 | def foo(): -633 633 | def bar(): pass - 634 |+ -634 635 | def baz(): -635 636 | pass -636 637 | # end +651 651 | # E306:3:5 +652 652 | def foo(): +653 653 | def bar(): pass + 654 |+ +654 655 | def baz(): +655 656 | pass +656 657 | # end -E30.py:641:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:661:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -639 | # E306 -640 | def f(): -641 | def f(): +659 | # E306 +660 | def f(): +661 | def f(): | ^^^ E306 -642 | pass -643 | # end +662 | pass +663 | # end | = help: Add missing blank line ℹ Safe fix -638 638 | -639 639 | # E306 -640 640 | def f(): - 641 |+ -641 642 | def f(): -642 643 | pass -643 644 | # end +658 658 | +659 659 | # E306 +660 660 | def f(): + 661 |+ +661 662 | def f(): +662 663 | pass +663 664 | # end -E30.py:649:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:669:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -647 | def a(): -648 | x = 2 -649 | @decorator +667 | def a(): +668 | x = 2 +669 | @decorator | ^ E306 -650 | def b(): -651 | pass +670 | def b(): +671 | pass | = help: Add missing blank line ℹ Safe fix -646 646 | # E306 -647 647 | def a(): -648 648 | x = 2 - 649 |+ -649 650 | @decorator -650 651 | def b(): -651 652 | pass +666 666 | # E306 +667 667 | def a(): +668 668 | x = 2 + 669 |+ +669 670 | @decorator +670 671 | def b(): +671 672 | pass -E30.py:658:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:678:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -656 | def a(): -657 | x = 2 -658 | @decorator +676 | def a(): +677 | x = 2 +678 | @decorator | ^ E306 -659 | async def b(): -660 | pass +679 | async def b(): +680 | pass | = help: Add missing blank line ℹ Safe fix -655 655 | # E306 -656 656 | def a(): -657 657 | x = 2 - 658 |+ -658 659 | @decorator -659 660 | async def b(): -660 661 | pass +675 675 | # E306 +676 676 | def a(): +677 677 | x = 2 + 678 |+ +678 679 | @decorator +679 680 | async def b(): +680 681 | pass -E30.py:667:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:687:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -665 | def a(): -666 | x = 2 -667 | async def b(): +685 | def a(): +686 | x = 2 +687 | async def b(): | ^^^^^ E306 -668 | pass -669 | # end +688 | pass +689 | # end | = help: Add missing blank line ℹ Safe fix -664 664 | # E306 -665 665 | def a(): -666 666 | x = 2 - 667 |+ -667 668 | async def b(): -668 669 | pass -669 670 | # end +684 684 | # E306 +685 685 | def a(): +686 686 | x = 2 + 687 |+ +687 688 | async def b(): +688 689 | pass +689 690 | # end From 9b15d8c0cf0d174f33acdb792780dfd51049862a Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 21 Nov 2023 23:48:38 +0900 Subject: [PATCH 039/122] Remove unnecessary clone derive. --- .../src/rules/pycodestyle/rules/logical_lines/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs index 325e4e82c6626..4a8bb4befd821 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs @@ -121,7 +121,7 @@ impl<'a> IntoIterator for &'a LogicalLines<'a> { /// 2 /// ] /// ``` -#[derive(Clone, Debug)] +#[derive(Debug)] pub(crate) struct LogicalLine<'a> { lines: &'a LogicalLines<'a>, line: &'a Line, From fd65c70130539302281e8916ab7beb371c16ca7b Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 24 Nov 2023 19:14:28 +0900 Subject: [PATCH 040/122] Update E306 cf https://github.com/PyCQA/pycodestyle/issues/1216 --- .../test/fixtures/pycodestyle/E30.py | 14 +- .../rules/logical_lines/blank_lines.rs | 5 +- ...ules__pycodestyle__tests__E301_E30.py.snap | 52 ++-- ...ules__pycodestyle__tests__E302_E30.py.snap | 250 +++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 196 +++++++------- ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 ++++----- ...ules__pycodestyle__tests__E306_E30.py.snap | 238 +++++++---------- 8 files changed, 433 insertions(+), 474 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 3439d9982389c..d09f9e67a95b0 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -349,6 +349,13 @@ class Class: # end +# no error +def f(): + def f(): + pass +# end + + # E301 class Class(object): @@ -656,13 +663,6 @@ def baz(): # end -# E306 -def f(): - def f(): - pass -# end - - # E306 def a(): x = 2 diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index c9db7a8d4961c..c925d56b57d69 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -315,7 +315,6 @@ impl AlwaysFixableViolation for BlankLinesAfterFunctionOrClass { /// Use instead: /// ```python /// def outer(): -/// /// def inner(): /// pass /// @@ -535,8 +534,8 @@ pub(crate) fn blank_lines( && !tracked_vars.follows_decorator // The class's docstring can directly precede the first function. && !tracked_vars.follows_docstring - // Do not trigger when the def/class follows an "indenting token" (if/while/etc...), unless that "indenting token" is a def. - && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level || tracked_vars.follows_def) + // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). + && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) // Allow groups of one-liners. && !(tracked_vars.follows_def && line diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index b5c16e49aa1a0..8924ed271d707 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:357:5: E301 [*] Expected 1 blank line, found 0 +E30.py:364:5: E301 [*] Expected 1 blank line, found 0 | -355 | def func1(): -356 | pass -357 | def func2(): +362 | def func1(): +363 | pass +364 | def func2(): | ^^^ E301 -358 | pass -359 | # end +365 | pass +366 | # end | = help: Add missing blank line(s) ℹ Safe fix -354 354 | -355 355 | def func1(): -356 356 | pass - 357 |+ -357 358 | def func2(): -358 359 | pass -359 360 | # end +361 361 | +362 362 | def func1(): +363 363 | pass + 364 |+ +364 365 | def func2(): +365 366 | pass +366 367 | # end -E30.py:368:5: E301 [*] Expected 1 blank line, found 0 +E30.py:375:5: E301 [*] Expected 1 blank line, found 0 | -366 | pass -367 | # comment -368 | def fn2(): +373 | pass +374 | # comment +375 | def fn2(): | ^^^ E301 -369 | pass -370 | # end +376 | pass +377 | # end | = help: Add missing blank line(s) ℹ Safe fix -364 364 | -365 365 | def fn1(): -366 366 | pass - 367 |+ -367 368 | # comment -368 369 | def fn2(): -369 370 | pass +371 371 | +372 372 | def fn1(): +373 373 | pass + 374 |+ +374 375 | # comment +375 376 | def fn2(): +376 377 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index dbcfe57ab650f..d9b81fcf89e81 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,34 +1,13 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:375:1: E302 [*] Expected 2 blank lines, found 0 - | -373 | # E302 -374 | """Main module.""" -375 | def fn(): - | ^^^ E302 -376 | pass -377 | # end - | - = help: Add missing blank line(s) - -ℹ Safe fix -372 372 | -373 373 | # E302 -374 374 | """Main module.""" - 375 |+ - 376 |+ -375 377 | def fn(): -376 378 | pass -377 379 | # end - E30.py:382:1: E302 [*] Expected 2 blank lines, found 0 | 380 | # E302 -381 | import sys -382 | def get_sys_path(): +381 | """Main module.""" +382 | def fn(): | ^^^ E302 -383 | return sys.path +383 | pass 384 | # end | = help: Add missing blank line(s) @@ -36,151 +15,172 @@ E30.py:382:1: E302 [*] Expected 2 blank lines, found 0 ℹ Safe fix 379 379 | 380 380 | # E302 -381 381 | import sys +381 381 | """Main module.""" 382 |+ 383 |+ -382 384 | def get_sys_path(): -383 385 | return sys.path +382 384 | def fn(): +383 385 | pass 384 386 | # end -E30.py:391:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:389:1: E302 [*] Expected 2 blank lines, found 0 | -389 | pass -390 | -391 | def b(): +387 | # E302 +388 | import sys +389 | def get_sys_path(): | ^^^ E302 -392 | pass -393 | # end +390 | return sys.path +391 | # end | = help: Add missing blank line(s) ℹ Safe fix -388 388 | def a(): -389 389 | pass -390 390 | - 391 |+ -391 392 | def b(): -392 393 | pass -393 394 | # end - -E30.py:402:1: E302 [*] Expected 2 blank lines, found 1 - | -400 | # comment -401 | -402 | def b(): +386 386 | +387 387 | # E302 +388 388 | import sys + 389 |+ + 390 |+ +389 391 | def get_sys_path(): +390 392 | return sys.path +391 393 | # end + +E30.py:398:1: E302 [*] Expected 2 blank lines, found 1 + | +396 | pass +397 | +398 | def b(): | ^^^ E302 -403 | pass -404 | # end +399 | pass +400 | # end | = help: Add missing blank line(s) ℹ Safe fix -397 397 | def a(): -398 398 | pass -399 399 | - 400 |+ -400 401 | # comment -401 402 | -402 403 | def b(): - -E30.py:411:1: E302 [*] Expected 2 blank lines, found 1 - | -409 | pass -410 | -411 | async def b(): - | ^^^^^ E302 -412 | pass -413 | # end +395 395 | def a(): +396 396 | pass +397 397 | + 398 |+ +398 399 | def b(): +399 400 | pass +400 401 | # end + +E30.py:409:1: E302 [*] Expected 2 blank lines, found 1 + | +407 | # comment +408 | +409 | def b(): + | ^^^ E302 +410 | pass +411 | # end | = help: Add missing blank line(s) ℹ Safe fix -408 408 | def a(): -409 409 | pass -410 410 | - 411 |+ -411 412 | async def b(): -412 413 | pass -413 414 | # end - -E30.py:420:1: E302 [*] Expected 2 blank lines, found 1 - | -418 | pass -419 | -420 | async def x(y: int = 1): +404 404 | def a(): +405 405 | pass +406 406 | + 407 |+ +407 408 | # comment +408 409 | +409 410 | def b(): + +E30.py:418:1: E302 [*] Expected 2 blank lines, found 1 + | +416 | pass +417 | +418 | async def b(): | ^^^^^ E302 -421 | pass -422 | # end +419 | pass +420 | # end | = help: Add missing blank line(s) ℹ Safe fix -417 417 | async def x(): -418 418 | pass -419 419 | - 420 |+ -420 421 | async def x(y: int = 1): -421 422 | pass -422 423 | # end - -E30.py:428:1: E302 [*] Expected 2 blank lines, found 0 - | -426 | def bar(): -427 | pass -428 | def baz(): pass - | ^^^ E302 +415 415 | def a(): +416 416 | pass +417 417 | + 418 |+ +418 419 | async def b(): +419 420 | pass +420 421 | # end + +E30.py:427:1: E302 [*] Expected 2 blank lines, found 1 + | +425 | pass +426 | +427 | async def x(y: int = 1): + | ^^^^^ E302 +428 | pass 429 | # end | = help: Add missing blank line(s) ℹ Safe fix -425 425 | # E302 -426 426 | def bar(): -427 427 | pass - 428 |+ - 429 |+ -428 430 | def baz(): pass -429 431 | # end -430 432 | - -E30.py:434:1: E302 [*] Expected 2 blank lines, found 0 - | -432 | # E302 -433 | def bar(): pass -434 | def baz(): +424 424 | async def x(): +425 425 | pass +426 426 | + 427 |+ +427 428 | async def x(y: int = 1): +428 429 | pass +429 430 | # end + +E30.py:435:1: E302 [*] Expected 2 blank lines, found 0 + | +433 | def bar(): +434 | pass +435 | def baz(): pass | ^^^ E302 -435 | pass 436 | # end | = help: Add missing blank line(s) ℹ Safe fix -431 431 | 432 432 | # E302 -433 433 | def bar(): pass - 434 |+ +433 433 | def bar(): +434 434 | pass 435 |+ -434 436 | def baz(): -435 437 | pass + 436 |+ +435 437 | def baz(): pass 436 438 | # end +437 439 | -E30.py:444:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:441:1: E302 [*] Expected 2 blank lines, found 0 + | +439 | # E302 +440 | def bar(): pass +441 | def baz(): + | ^^^ E302 +442 | pass +443 | # end | -443 | # comment -444 | @decorator + = help: Add missing blank line(s) + +ℹ Safe fix +438 438 | +439 439 | # E302 +440 440 | def bar(): pass + 441 |+ + 442 |+ +441 443 | def baz(): +442 444 | pass +443 445 | # end + +E30.py:451:1: E302 [*] Expected 2 blank lines, found 1 + | +450 | # comment +451 | @decorator | ^ E302 -445 | def g(): -446 | pass +452 | def g(): +453 | pass | = help: Add missing blank line(s) ℹ Safe fix -440 440 | def f(): -441 441 | pass -442 442 | - 443 |+ -443 444 | # comment -444 445 | @decorator -445 446 | def g(): +447 447 | def f(): +448 448 | pass +449 449 | + 450 |+ +450 451 | # comment +451 452 | @decorator +452 453 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 29c3ce985c118..d20f62dd570ea 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:455:5: E303 [*] Too many blank lines (2) +E30.py:462:5: E303 [*] Too many blank lines (2) | -455 | # arbitrary comment +462 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -456 | -457 | def inner(): # E306 not expected +463 | +464 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -451 451 | def fn(): -452 452 | _ = None -453 453 | -454 |- -455 454 | # arbitrary comment -456 455 | -457 456 | def inner(): # E306 not expected +458 458 | def fn(): +459 459 | _ = None +460 460 | +461 |- +462 461 | # arbitrary comment +463 462 | +464 463 | def inner(): # E306 not expected -E30.py:467:5: E303 [*] Too many blank lines (2) +E30.py:474:5: E303 [*] Too many blank lines (2) | -467 | # arbitrary comment +474 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -468 | def inner(): # E306 not expected -469 | pass +475 | def inner(): # E306 not expected +476 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -463 463 | def fn(): -464 464 | _ = None -465 465 | -466 |- -467 466 | # arbitrary comment -468 467 | def inner(): # E306 not expected -469 468 | pass +470 470 | def fn(): +471 471 | _ = None +472 472 | +473 |- +474 473 | # arbitrary comment +475 474 | def inner(): # E306 not expected +476 475 | pass -E30.py:478:1: E303 [*] Too many blank lines (3) +E30.py:485:1: E303 [*] Too many blank lines (3) | -478 | print() +485 | print() | ^^^^^ E303 -479 | # end +486 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -474 474 | print() -475 475 | -476 476 | -477 |- -478 477 | print() -479 478 | # end -480 479 | +481 481 | print() +482 482 | +483 483 | +484 |- +485 484 | print() +486 485 | # end +487 486 | -E30.py:487:1: E303 [*] Too many blank lines (3) +E30.py:494:1: E303 [*] Too many blank lines (3) | -487 | # comment +494 | # comment | ^^^^^^^^^ E303 -488 | -489 | print() +495 | +496 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -483 483 | print() -484 484 | -485 485 | -486 |- -487 486 | # comment -488 487 | -489 488 | print() +490 490 | print() +491 491 | +492 492 | +493 |- +494 493 | # comment +495 494 | +496 495 | print() -E30.py:498:5: E303 [*] Too many blank lines (2) +E30.py:505:5: E303 [*] Too many blank lines (2) | -498 | # comment +505 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -494 494 | def a(): -495 495 | print() -496 496 | -497 |- -498 497 | # comment -499 498 | -500 499 | +501 501 | def a(): +502 502 | print() +503 503 | +504 |- +505 504 | # comment +506 505 | +507 506 | -E30.py:501:5: E303 [*] Too many blank lines (2) +E30.py:508:5: E303 [*] Too many blank lines (2) | -501 | # another comment +508 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -502 | -503 | print() +509 | +510 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -497 497 | -498 498 | # comment -499 499 | -500 |- -501 500 | # another comment -502 501 | -503 502 | print() - -E30.py:512:1: E303 [*] Too many blank lines (3) - | -512 | / """This class docstring comes on line 5. -513 | | It gives error E303: too many blank lines (3) -514 | | """ +504 504 | +505 505 | # comment +506 506 | +507 |- +508 507 | # another comment +509 508 | +510 509 | print() + +E30.py:519:1: E303 [*] Too many blank lines (3) + | +519 | / """This class docstring comes on line 5. +520 | | It gives error E303: too many blank lines (3) +521 | | """ | |___^ E303 -515 | # end +522 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -508 508 | #!python -509 509 | -510 510 | -511 |- -512 511 | """This class docstring comes on line 5. -513 512 | It gives error E303: too many blank lines (3) -514 513 | """ +515 515 | #!python +516 516 | +517 517 | +518 |- +519 518 | """This class docstring comes on line 5. +520 519 | It gives error E303: too many blank lines (3) +521 520 | """ -E30.py:524:5: E303 [*] Too many blank lines (2) +E30.py:531:5: E303 [*] Too many blank lines (2) | -524 | def b(self): +531 | def b(self): | ^^^ E303 -525 | pass -526 | # end +532 | pass +533 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -520 520 | def a(self): -521 521 | pass -522 522 | -523 |- -524 523 | def b(self): -525 524 | pass -526 525 | # end +527 527 | def a(self): +528 528 | pass +529 529 | +530 |- +531 530 | def b(self): +532 531 | pass +533 532 | # end -E30.py:534:5: E303 [*] Too many blank lines (2) +E30.py:541:5: E303 [*] Too many blank lines (2) | -534 | a = 2 +541 | a = 2 | ^ E303 -535 | # end +542 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -530 530 | if True: -531 531 | a = 1 -532 532 | -533 |- -534 533 | a = 2 -535 534 | # end -536 535 | +537 537 | if True: +538 538 | a = 1 +539 539 | +540 |- +541 540 | a = 2 +542 541 | # end +543 542 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index b65d50e537bcb..77a17561b882d 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:541:1: E304 [*] blank lines found after function decorator +E30.py:548:1: E304 [*] blank lines found after function decorator | -539 | @decorator -540 | -541 | def function(): +546 | @decorator +547 | +548 | def function(): | ^^^ E304 -542 | pass -543 | # end +549 | pass +550 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -537 537 | -538 538 | # E304 -539 539 | @decorator -540 |- -541 540 | def function(): -542 541 | pass -543 542 | # end +544 544 | +545 545 | # E304 +546 546 | @decorator +547 |- +548 547 | def function(): +549 548 | pass +550 549 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 87e987f761575..6fc6b93e89781 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:553:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:560:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -552 | # another comment -553 | fn() +559 | # another comment +560 | fn() | ^^ E305 -554 | # end +561 | # end | = help: Add missing blank line(s) ℹ Safe fix -550 550 | # comment -551 551 | -552 552 | # another comment - 553 |+ - 554 |+ -553 555 | fn() -554 556 | # end -555 557 | +557 557 | # comment +558 558 | +559 559 | # another comment + 560 |+ + 561 |+ +560 562 | fn() +561 563 | # end +562 564 | -E30.py:564:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:571:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -563 | # another comment -564 | a = 1 +570 | # another comment +571 | a = 1 | ^ E305 -565 | # end +572 | # end | = help: Add missing blank line(s) ℹ Safe fix -561 561 | # comment -562 562 | -563 563 | # another comment - 564 |+ - 565 |+ -564 566 | a = 1 -565 567 | # end -566 568 | +568 568 | # comment +569 569 | +570 570 | # another comment + 571 |+ + 572 |+ +571 573 | a = 1 +572 574 | # end +573 575 | -E30.py:576:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:583:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -574 | # another comment -575 | -576 | try: +581 | # another comment +582 | +583 | try: | ^^^ E305 -577 | fn() -578 | except Exception: +584 | fn() +585 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -573 573 | -574 574 | # another comment -575 575 | - 576 |+ -576 577 | try: -577 578 | fn() -578 579 | except Exception: +580 580 | +581 581 | # another comment +582 582 | + 583 |+ +583 584 | try: +584 585 | fn() +585 586 | except Exception: -E30.py:588:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:595:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -587 | # Two spaces before comments, too. -588 | if a(): +594 | # Two spaces before comments, too. +595 | if a(): | ^^ E305 -589 | a() -590 | # end +596 | a() +597 | # end | = help: Add missing blank line(s) ℹ Safe fix -585 585 | print -586 586 | -587 587 | # Two spaces before comments, too. - 588 |+ - 589 |+ -588 590 | if a(): -589 591 | a() -590 592 | # end +592 592 | print +593 593 | +594 594 | # Two spaces before comments, too. + 595 |+ + 596 |+ +595 597 | if a(): +596 598 | a() +597 599 | # end -E30.py:601:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:608:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -599 | blah, blah -600 | -601 | if __name__ == '__main__': +606 | blah, blah +607 | +608 | if __name__ == '__main__': | ^^ E305 -602 | main() -603 | # end +609 | main() +610 | # end | = help: Add missing blank line(s) ℹ Safe fix -598 598 | def main(): -599 599 | blah, blah -600 600 | - 601 |+ -601 602 | if __name__ == '__main__': -602 603 | main() -603 604 | # end +605 605 | def main(): +606 606 | blah, blah +607 607 | + 608 |+ +608 609 | if __name__ == '__main__': +609 610 | main() +610 611 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index d045a07886b18..3b69bf5ca4be6 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,190 +1,150 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:609:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:616:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -607 | def a(): -608 | x = 1 -609 | def b(): +614 | def a(): +615 | x = 1 +616 | def b(): | ^^^ E306 -610 | pass -611 | # end +617 | pass +618 | # end | = help: Add missing blank line ℹ Safe fix -606 606 | # E306:3:5 -607 607 | def a(): -608 608 | x = 1 - 609 |+ -609 610 | def b(): -610 611 | pass -611 612 | # end - -E30.py:617:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -615 | async def a(): -616 | x = 1 -617 | def b(): +613 613 | # E306:3:5 +614 614 | def a(): +615 615 | x = 1 + 616 |+ +616 617 | def b(): +617 618 | pass +618 619 | # end + +E30.py:624:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +622 | async def a(): +623 | x = 1 +624 | def b(): | ^^^ E306 -618 | pass -619 | # end +625 | pass +626 | # end | = help: Add missing blank line ℹ Safe fix -614 614 | #: E306:3:5 -615 615 | async def a(): -616 616 | x = 1 - 617 |+ -617 618 | def b(): -618 619 | pass -619 620 | # end - -E30.py:625:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -623 | def a(): -624 | x = 2 -625 | def b(): +621 621 | #: E306:3:5 +622 622 | async def a(): +623 623 | x = 1 + 624 |+ +624 625 | def b(): +625 626 | pass +626 627 | # end + +E30.py:632:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +630 | def a(): +631 | x = 2 +632 | def b(): | ^^^ E306 -626 | x = 1 -627 | def c(): +633 | x = 1 +634 | def c(): | = help: Add missing blank line ℹ Safe fix -622 622 | #: E306:3:5 E306:5:9 -623 623 | def a(): -624 624 | x = 2 - 625 |+ -625 626 | def b(): -626 627 | x = 1 -627 628 | def c(): - -E30.py:627:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -625 | def b(): -626 | x = 1 -627 | def c(): +629 629 | #: E306:3:5 E306:5:9 +630 630 | def a(): +631 631 | x = 2 + 632 |+ +632 633 | def b(): +633 634 | x = 1 +634 635 | def c(): + +E30.py:634:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +632 | def b(): +633 | x = 1 +634 | def c(): | ^^^ E306 -628 | pass -629 | # end +635 | pass +636 | # end | = help: Add missing blank line ℹ Safe fix -624 624 | x = 2 -625 625 | def b(): -626 626 | x = 1 - 627 |+ -627 628 | def c(): -628 629 | pass -629 630 | # end - -E30.py:635:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -633 | def a(): -634 | x = 1 -635 | class C: +631 631 | x = 2 +632 632 | def b(): +633 633 | x = 1 + 634 |+ +634 635 | def c(): +635 636 | pass +636 637 | # end + +E30.py:642:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +640 | def a(): +641 | x = 1 +642 | class C: | ^^^^^ E306 -636 | pass -637 | x = 2 +643 | pass +644 | x = 2 | = help: Add missing blank line ℹ Safe fix -632 632 | # E306:3:5 E306:6:5 -633 633 | def a(): -634 634 | x = 1 - 635 |+ -635 636 | class C: -636 637 | pass -637 638 | x = 2 - -E30.py:638:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -636 | pass -637 | x = 2 -638 | def b(): - | ^^^ E306 -639 | pass -640 | # end - | - = help: Add missing blank line - -ℹ Safe fix -635 635 | class C: -636 636 | pass -637 637 | x = 2 - 638 |+ -638 639 | def b(): -639 640 | pass -640 641 | # end +639 639 | # E306:3:5 E306:6:5 +640 640 | def a(): +641 641 | x = 1 + 642 |+ +642 643 | class C: +643 644 | pass +644 645 | x = 2 E30.py:645:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -643 | # E306 -644 | def foo(): -645 | def bar(): +643 | pass +644 | x = 2 +645 | def b(): | ^^^ E306 646 | pass -647 | def baz(): pass +647 | # end | = help: Add missing blank line ℹ Safe fix -642 642 | -643 643 | # E306 -644 644 | def foo(): +642 642 | class C: +643 643 | pass +644 644 | x = 2 645 |+ -645 646 | def bar(): +645 646 | def b(): 646 647 | pass -647 648 | def baz(): pass - -E30.py:647:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -645 | def bar(): -646 | pass -647 | def baz(): pass - | ^^^ E306 -648 | # end - | - = help: Add missing blank line - -ℹ Safe fix -644 644 | def foo(): -645 645 | def bar(): -646 646 | pass - 647 |+ -647 648 | def baz(): pass -648 649 | # end -649 650 | +647 648 | # end E30.py:654:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -652 | def foo(): -653 | def bar(): pass -654 | def baz(): +652 | def bar(): +653 | pass +654 | def baz(): pass | ^^^ E306 -655 | pass -656 | # end +655 | # end | = help: Add missing blank line ℹ Safe fix -651 651 | # E306:3:5 -652 652 | def foo(): -653 653 | def bar(): pass +651 651 | def foo(): +652 652 | def bar(): +653 653 | pass 654 |+ -654 655 | def baz(): -655 656 | pass -656 657 | # end +654 655 | def baz(): pass +655 656 | # end +656 657 | E30.py:661:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -659 | # E306 -660 | def f(): -661 | def f(): +659 | def foo(): +660 | def bar(): pass +661 | def baz(): | ^^^ E306 662 | pass 663 | # end @@ -192,11 +152,11 @@ E30.py:661:5: E306 [*] Expected 1 blank line before a nested definition, found 0 = help: Add missing blank line ℹ Safe fix -658 658 | -659 659 | # E306 -660 660 | def f(): +658 658 | # E306:3:5 +659 659 | def foo(): +660 660 | def bar(): pass 661 |+ -661 662 | def f(): +661 662 | def baz(): 662 663 | pass 663 664 | # end From 883818b189fb4cc1abf689bc48365f3476e4f929 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 28 Nov 2023 12:55:26 +0900 Subject: [PATCH 041/122] is_first_logical_line -> is_not_first_logical_line to be able to derive Default. --- .../rules/logical_lines/blank_lines.rs | 29 ++++--------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index c925d56b57d69..064b1456f0f59 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -14,7 +14,7 @@ use crate::checkers::logical_lines::LogicalLinesContext; use super::LogicalLine; /// Contains variables used for the linting of blank lines. -#[derive(Debug)] +#[derive(Debug, Default)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct BlankLinesTrackingVars { follows_decorator: bool, @@ -27,7 +27,7 @@ pub(crate) struct BlankLinesTrackingVars { /// The indent level where the function started. fn_indent_level: usize, /// First line that is not a comment. - is_first_logical_line: bool, + is_not_first_logical_line: bool, /// This needs to be tracked between lines since the `is_in_class` and `is_in_fn` are set to /// false when a comment is set dedented, but E305 should trigger on the next non-comment line. follows_comment_after_fn: bool, @@ -38,25 +38,6 @@ pub(crate) struct BlankLinesTrackingVars { previous_unindented_token: Option, } -impl Default for BlankLinesTrackingVars { - fn default() -> BlankLinesTrackingVars { - BlankLinesTrackingVars { - follows_decorator: false, - follows_def: false, - follows_docstring: false, - is_in_class: false, - class_indent_level: 0, - is_in_fn: false, - fn_indent_level: 0, - is_first_logical_line: true, - follows_comment_after_fn: false, - follows_comment_after_class: false, - last_non_comment_line_end: TextSize::new(0), - previous_unindented_token: None, - } - } -} - /// Number of blank lines between various code parts. struct BlankLinesConfig; @@ -414,7 +395,7 @@ pub(crate) fn blank_lines( } // Don't expect blank lines before the first non comment line. - if !tracked_vars.is_first_logical_line { + if tracked_vars.is_not_first_logical_line { if line.line.preceding_blank_lines == 0 // Only applies to methods. && token.kind() == TokenKind::Def @@ -595,8 +576,8 @@ pub(crate) fn blank_lines( } if !line_is_comment_only { - if tracked_vars.is_first_logical_line { - tracked_vars.is_first_logical_line = false; + if !tracked_vars.is_not_first_logical_line { + tracked_vars.is_not_first_logical_line = true; } tracked_vars.follows_docstring = is_docstring(Some(line)); From 29f0e6b4de8a87135321a6c0df94fabc62facded Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 28 Nov 2023 12:59:54 +0900 Subject: [PATCH 042/122] Remove BlankLinesConfig struct and use module level constants instead. --- .../rules/logical_lines/blank_lines.rs | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 064b1456f0f59..d7485308a82ad 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -38,15 +38,10 @@ pub(crate) struct BlankLinesTrackingVars { previous_unindented_token: Option, } -/// Number of blank lines between various code parts. -struct BlankLinesConfig; - -impl BlankLinesConfig { - /// Number of blank lines around top level classes and functions. - const TOP_LEVEL: u32 = 2; - /// Number of blank lines around methods and nested classes and functions. - const METHOD: u32 = 1; -} +/// Number of blank lines around top level classes and functions. +const BLANK_LINES_TOP_LEVEL: u32 = 2; +/// Number of blank lines around methods and nested classes and functions. +const BLANK_LINES_METHOD_LEVEL: u32 = 1; /// ## What it does /// Checks for missing blank lines between methods of a class. @@ -87,7 +82,7 @@ impl AlwaysFixableViolation for BlankLineBetweenMethods { let BlankLineBetweenMethods(nb_blank_lines) = self; format!( "Expected {:?} blank line, found {nb_blank_lines}", - BlankLinesConfig::METHOD + BLANK_LINES_METHOD_LEVEL ) } @@ -134,7 +129,7 @@ impl AlwaysFixableViolation for BlankLinesTopLevel { let BlankLinesTopLevel(nb_blank_lines) = self; format!( "Expected {:?} blank lines, found {nb_blank_lines}", - BlankLinesConfig::TOP_LEVEL + BLANK_LINES_TOP_LEVEL ) } @@ -420,7 +415,7 @@ pub(crate) fn blank_lines( context.push_diagnostic(diagnostic); } - if line.line.preceding_blank_lines < BlankLinesConfig::TOP_LEVEL + if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !tracked_vars.follows_decorator // Allow groups of one-liners. @@ -441,26 +436,28 @@ pub(crate) fn blank_lines( token.range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string().repeat( - (BlankLinesConfig::TOP_LEVEL - line.line.preceding_blank_lines) as usize, - ), + stylist + .line_ending() + .as_str() + .to_string() + .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), locator.line_start(tracked_vars.last_non_comment_line_end), ))); context.push_diagnostic(diagnostic); } - if line.line.blank_lines > BlankLinesConfig::TOP_LEVEL - || (indent_level > 0 && line.line.blank_lines > BlankLinesConfig::METHOD) + if line.line.blank_lines > BLANK_LINES_TOP_LEVEL + || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) { // E303 let mut diagnostic = Diagnostic::new(TooManyBlankLines(line.line.blank_lines), token.range); let chars_to_remove = if indent_level > 0 { - line.line.preceding_blank_characters - BlankLinesConfig::METHOD + line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL } else { - line.line.preceding_blank_characters - BlankLinesConfig::TOP_LEVEL + line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL }; let end = locator.line_start(token.range.start()); let start = end - TextSize::new(chars_to_remove); @@ -483,7 +480,7 @@ pub(crate) fn blank_lines( context.push_diagnostic(diagnostic); } - if line.line.preceding_blank_lines < BlankLinesConfig::TOP_LEVEL + if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL && is_top_level_token(tracked_vars.previous_unindented_token) && indent_level == 0 && !line_is_comment_only @@ -500,7 +497,7 @@ pub(crate) fn blank_lines( .line_ending() .as_str() .to_string() - .repeat((BlankLinesConfig::TOP_LEVEL - line.line.blank_lines) as usize), + .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), locator.line_start(token.range.start()), ))); From 0b6bfdf351374aa1fa04eb3cd320ea4baa5ab8ab Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 28 Nov 2023 15:24:57 +0900 Subject: [PATCH 043/122] Use an enum to keep track of class and fn status. --- .../rules/logical_lines/blank_lines.rs | 80 ++++++++++--------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index d7485308a82ad..d229c132045c0 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -20,18 +20,10 @@ pub(crate) struct BlankLinesTrackingVars { follows_decorator: bool, follows_def: bool, follows_docstring: bool, - is_in_class: bool, - /// The indent level where the class started. - class_indent_level: usize, - is_in_fn: bool, - /// The indent level where the function started. - fn_indent_level: usize, + fn_status: Status, + class_status: Status, /// First line that is not a comment. is_not_first_logical_line: bool, - /// This needs to be tracked between lines since the `is_in_class` and `is_in_fn` are set to - /// false when a comment is set dedented, but E305 should trigger on the next non-comment line. - follows_comment_after_fn: bool, - follows_comment_after_class: bool, /// Used for the fix in case a comment separates two non-comment logical lines to make the comment "stick" /// to the second line instead of the first. last_non_comment_line_end: TextSize, @@ -43,6 +35,16 @@ const BLANK_LINES_TOP_LEVEL: u32 = 2; /// Number of blank lines around methods and nested classes and functions. const BLANK_LINES_METHOD_LEVEL: u32 = 1; +#[derive(Copy, Clone, Debug, Default)] +enum Status { + /// Stores the indent level where the nesting started. + Inside(usize), + /// This is used to rectify a Inside switched to a Outside because of a dedented comment. + CommentAfter(usize), + #[default] + Outside, +} + /// ## What it does /// Checks for missing blank lines between methods of a class. /// @@ -354,34 +356,42 @@ pub(crate) fn blank_lines( ) { let line_is_comment_only = line.is_comment_only(); - if indent_level < tracked_vars.class_indent_level && tracked_vars.is_in_class { - tracked_vars.is_in_class = false; - if line_is_comment_only { - tracked_vars.follows_comment_after_class = true; + if let Status::Inside(nesting_indent) = tracked_vars.class_status { + if indent_level < nesting_indent { + if line_is_comment_only { + tracked_vars.class_status = Status::CommentAfter(nesting_indent); + } else { + tracked_vars.class_status = Status::Outside; + } } } - if indent_level < tracked_vars.fn_indent_level && tracked_vars.is_in_fn { - tracked_vars.is_in_fn = false; - if line_is_comment_only { - tracked_vars.follows_comment_after_fn = true; + if let Status::Inside(nesting_indent) = tracked_vars.fn_status { + if indent_level < nesting_indent { + if line_is_comment_only { + tracked_vars.fn_status = Status::CommentAfter(nesting_indent); + } else { + tracked_vars.fn_status = Status::Outside; + } } } // A comment can be de-indented while still being in a class/function, in that case // we need to revert the variables. - if tracked_vars.follows_comment_after_fn && !line_is_comment_only { - if indent_level == tracked_vars.fn_indent_level { - tracked_vars.is_in_fn = true; + if !line_is_comment_only { + if let Status::CommentAfter(indent) = tracked_vars.fn_status { + if indent_level >= indent { + tracked_vars.fn_status = Status::Inside(indent) + } + tracked_vars.fn_status = Status::Outside } - tracked_vars.follows_comment_after_fn = false; - } - if tracked_vars.follows_comment_after_class && !line_is_comment_only { - if indent_level == tracked_vars.class_indent_level { - tracked_vars.is_in_class = true; + if let Status::CommentAfter(indent) = tracked_vars.class_status { + if indent_level >= indent { + tracked_vars.class_status = Status::Inside(indent) + } + tracked_vars.class_status = Status::Outside } - tracked_vars.follows_comment_after_class = false; } for token in line.tokens() { @@ -394,7 +404,7 @@ pub(crate) fn blank_lines( if line.line.preceding_blank_lines == 0 // Only applies to methods. && token.kind() == TokenKind::Def - && tracked_vars.is_in_class + && matches!(tracked_vars.class_status, Status::Inside(_)) // The class/parent method's docstring can directly precede the def. && !tracked_vars.follows_docstring // Do not trigger when the def follows an if/while/etc... @@ -505,8 +515,8 @@ pub(crate) fn blank_lines( } if line.line.preceding_blank_lines == 0 - // Only apply to nested functions. - && tracked_vars.is_in_fn + // Only apply to nested functions. + && matches!(tracked_vars.fn_status, Status::Inside(_)) && is_top_level_token_or_decorator(token.kind) // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !tracked_vars.follows_decorator @@ -539,10 +549,9 @@ pub(crate) fn blank_lines( match token.kind() { TokenKind::Class => { - if !tracked_vars.is_in_class { - tracked_vars.class_indent_level = indent_level + indent_size; + if matches!(tracked_vars.class_status, Status::Outside) { + tracked_vars.class_status = Status::Inside(indent_level + indent_size); } - tracked_vars.is_in_class = true; tracked_vars.follows_decorator = false; tracked_vars.follows_def = false; break; @@ -553,10 +562,9 @@ pub(crate) fn blank_lines( break; } TokenKind::Def | TokenKind::Async => { - if !tracked_vars.is_in_fn { - tracked_vars.fn_indent_level = indent_level + indent_size; + if matches!(tracked_vars.fn_status, Status::Outside) { + tracked_vars.fn_status = Status::Inside(indent_level + indent_size); } - tracked_vars.is_in_fn = true; tracked_vars.follows_def = true; tracked_vars.follows_decorator = false; break; From 304115aec19d32e5c3c2a5097b140324375baa2d Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 28 Nov 2023 16:00:03 +0900 Subject: [PATCH 044/122] Remove for loop, clippy fixes. --- .../rules/logical_lines/blank_lines.rs | 291 +++++++++--------- 1 file changed, 141 insertions(+), 150 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index d229c132045c0..46f0a0e89d6fc 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -7,6 +7,7 @@ use ruff_macros::{derive_message_formats, violation}; use ruff_python_codegen::Stylist; use ruff_python_parser::TokenKind; use ruff_source_file::Locator; + use ruff_text_size::TextSize; use crate::checkers::logical_lines::LogicalLinesContext; @@ -82,10 +83,7 @@ impl AlwaysFixableViolation for BlankLineBetweenMethods { #[derive_message_formats] fn message(&self) -> String { let BlankLineBetweenMethods(nb_blank_lines) = self; - format!( - "Expected {:?} blank line, found {nb_blank_lines}", - BLANK_LINES_METHOD_LEVEL - ) + format!("Expected {BLANK_LINES_METHOD_LEVEL:?} blank line, found {nb_blank_lines}") } fn fix_title(&self) -> String { @@ -129,10 +127,7 @@ impl AlwaysFixableViolation for BlankLinesTopLevel { #[derive_message_formats] fn message(&self) -> String { let BlankLinesTopLevel(nb_blank_lines) = self; - format!( - "Expected {:?} blank lines, found {nb_blank_lines}", - BLANK_LINES_TOP_LEVEL - ) + format!("Expected {BLANK_LINES_TOP_LEVEL:?} blank lines, found {nb_blank_lines}") } fn fix_title(&self) -> String { @@ -381,29 +376,32 @@ pub(crate) fn blank_lines( if !line_is_comment_only { if let Status::CommentAfter(indent) = tracked_vars.fn_status { if indent_level >= indent { - tracked_vars.fn_status = Status::Inside(indent) + tracked_vars.fn_status = Status::Inside(indent); } - tracked_vars.fn_status = Status::Outside + tracked_vars.fn_status = Status::Outside; } if let Status::CommentAfter(indent) = tracked_vars.class_status { if indent_level >= indent { - tracked_vars.class_status = Status::Inside(indent) + tracked_vars.class_status = Status::Inside(indent); } - tracked_vars.class_status = Status::Outside + tracked_vars.class_status = Status::Outside; } } - - for token in line.tokens() { - if matches!(token.kind, TokenKind::Indent | TokenKind::Dedent) { - continue; - } - - // Don't expect blank lines before the first non comment line. - if tracked_vars.is_not_first_logical_line { - if line.line.preceding_blank_lines == 0 + let first_token = match line + .tokens() + .iter() + .find(|token| !matches!(token.kind, TokenKind::Indent | TokenKind::Dedent)) + { + Some(token) => token, + None => return, + }; + + // Don't expect blank lines before the first non comment line. + if tracked_vars.is_not_first_logical_line { + if line.line.preceding_blank_lines == 0 // Only applies to methods. - && token.kind() == TokenKind::Def + && first_token.kind() == TokenKind::Def && matches!(tracked_vars.class_status, Status::Inside(_)) // The class/parent method's docstring can directly precede the def. && !tracked_vars.follows_docstring @@ -411,21 +409,21 @@ pub(crate) fn blank_lines( && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !tracked_vars.follows_decorator - { - // E301 - let mut diagnostic = Diagnostic::new( - BlankLineBetweenMethods(line.line.preceding_blank_lines), - token.range, - ); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), - locator.line_start(tracked_vars.last_non_comment_line_end), - ))); - - context.push_diagnostic(diagnostic); - } + { + // E301 + let mut diagnostic = Diagnostic::new( + BlankLineBetweenMethods(line.line.preceding_blank_lines), + first_token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(tracked_vars.last_non_comment_line_end), + ))); + + context.push_diagnostic(diagnostic); + } - if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !tracked_vars.follows_decorator // Allow groups of one-liners. @@ -438,86 +436,86 @@ pub(crate) fn blank_lines( // Only trigger on non-indented classes and functions (for example functions within an if are ignored) && indent_level == 0 // Only apply to functions or classes. - && is_top_level_token_or_decorator(token.kind) - { - // E302 - let mut diagnostic = Diagnostic::new( - BlankLinesTopLevel(line.line.preceding_blank_lines), - token.range, - ); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .as_str() - .to_string() - .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), - locator.line_start(tracked_vars.last_non_comment_line_end), - ))); - - context.push_diagnostic(diagnostic); - } + && is_top_level_token_or_decorator(first_token.kind) + { + // E302 + let mut diagnostic = Diagnostic::new( + BlankLinesTopLevel(line.line.preceding_blank_lines), + first_token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .as_str() + .to_string() + .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), + locator.line_start(tracked_vars.last_non_comment_line_end), + ))); + + context.push_diagnostic(diagnostic); + } - if line.line.blank_lines > BLANK_LINES_TOP_LEVEL - || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) - { - // E303 - let mut diagnostic = - Diagnostic::new(TooManyBlankLines(line.line.blank_lines), token.range); - - let chars_to_remove = if indent_level > 0 { - line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL - } else { - line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL - }; - let end = locator.line_start(token.range.start()); - let start = end - TextSize::new(chars_to_remove); - diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); - - context.push_diagnostic(diagnostic); - } + if line.line.blank_lines > BLANK_LINES_TOP_LEVEL + || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) + { + // E303 + let mut diagnostic = + Diagnostic::new(TooManyBlankLines(line.line.blank_lines), first_token.range); - if tracked_vars.follows_decorator && line.line.preceding_blank_lines > 0 { - // E304 - let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, token.range); + let chars_to_remove = if indent_level > 0 { + line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL + } else { + line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL + }; + let end = locator.line_start(first_token.range.start()); + let start = end - TextSize::new(chars_to_remove); + diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); - let range = token.range; - diagnostic.set_fix(Fix::safe_edit(Edit::deletion( - locator.line_start(range.start()) - - TextSize::new(line.line.preceding_blank_characters), - locator.line_start(range.start()), - ))); + context.push_diagnostic(diagnostic); + } - context.push_diagnostic(diagnostic); - } + if tracked_vars.follows_decorator && line.line.preceding_blank_lines > 0 { + // E304 + let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, first_token.range); - if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL - && is_top_level_token(tracked_vars.previous_unindented_token) - && indent_level == 0 - && !line_is_comment_only - && !is_top_level_token_or_decorator(token.kind) - { - // E305 - let mut diagnostic = Diagnostic::new( - BlankLinesAfterFunctionOrClass(line.line.blank_lines), - token.range, - ); - - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .as_str() - .to_string() - .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), - locator.line_start(token.range.start()), - ))); - - context.push_diagnostic(diagnostic); - } + let range = first_token.range; + diagnostic.set_fix(Fix::safe_edit(Edit::deletion( + locator.line_start(range.start()) + - TextSize::new(line.line.preceding_blank_characters), + locator.line_start(range.start()), + ))); - if line.line.preceding_blank_lines == 0 + context.push_diagnostic(diagnostic); + } + + if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + && is_top_level_token(tracked_vars.previous_unindented_token) + && indent_level == 0 + && !line_is_comment_only + && !is_top_level_token_or_decorator(first_token.kind) + { + // E305 + let mut diagnostic = Diagnostic::new( + BlankLinesAfterFunctionOrClass(line.line.blank_lines), + first_token.range, + ); + + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .as_str() + .to_string() + .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), + locator.line_start(first_token.range.start()), + ))); + + context.push_diagnostic(diagnostic); + } + + if line.line.preceding_blank_lines == 0 // Only apply to nested functions. && matches!(tracked_vars.fn_status, Status::Inside(_)) - && is_top_level_token_or_decorator(token.kind) + && is_top_level_token_or_decorator(first_token.kind) // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !tracked_vars.follows_decorator // The class's docstring can directly precede the first function. @@ -531,52 +529,45 @@ pub(crate) fn blank_lines( .last() .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) ) - { - // E306 - let mut diagnostic = Diagnostic::new( - BlankLinesBeforeNestedDefinition(line.line.blank_lines), - token.range, - ); - - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), - locator.line_start(token.range.start()), - ))); - - context.push_diagnostic(diagnostic); - } + { + // E306 + let mut diagnostic = Diagnostic::new( + BlankLinesBeforeNestedDefinition(line.line.blank_lines), + first_token.range, + ); + + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(first_token.range.start()), + ))); + + context.push_diagnostic(diagnostic); } + } - match token.kind() { - TokenKind::Class => { - if matches!(tracked_vars.class_status, Status::Outside) { - tracked_vars.class_status = Status::Inside(indent_level + indent_size); - } - tracked_vars.follows_decorator = false; - tracked_vars.follows_def = false; - break; - } - TokenKind::At => { - tracked_vars.follows_decorator = true; - tracked_vars.follows_def = false; - break; - } - TokenKind::Def | TokenKind::Async => { - if matches!(tracked_vars.fn_status, Status::Outside) { - tracked_vars.fn_status = Status::Inside(indent_level + indent_size); - } - tracked_vars.follows_def = true; - tracked_vars.follows_decorator = false; - break; + match first_token.kind() { + TokenKind::Class => { + if matches!(tracked_vars.class_status, Status::Outside) { + tracked_vars.class_status = Status::Inside(indent_level + indent_size); } - TokenKind::Comment => { - break; - } - _ => { - tracked_vars.follows_decorator = false; - tracked_vars.follows_def = false; - break; + tracked_vars.follows_decorator = false; + tracked_vars.follows_def = false; + } + TokenKind::At => { + tracked_vars.follows_decorator = true; + tracked_vars.follows_def = false; + } + TokenKind::Def | TokenKind::Async => { + if matches!(tracked_vars.fn_status, Status::Outside) { + tracked_vars.fn_status = Status::Inside(indent_level + indent_size); } + tracked_vars.follows_def = true; + tracked_vars.follows_decorator = false; + } + TokenKind::Comment => {} + _ => { + tracked_vars.follows_decorator = false; + tracked_vars.follows_def = false; } } From 083f4f5fa007f89860f57639efad06d483640ca0 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 28 Nov 2023 16:09:10 +0900 Subject: [PATCH 045/122] Remove unnecessary Option from is_docstring. --- .../rules/logical_lines/blank_lines.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 46f0a0e89d6fc..0600b9cf1df46 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -314,14 +314,12 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { } /// Returns `true` if line is a docstring only line. -fn is_docstring(line: Option<&LogicalLine>) -> bool { - line.is_some_and(|line| { - !line.tokens_trimmed().is_empty() - && line - .tokens_trimmed() - .iter() - .all(|token| matches!(token.kind(), TokenKind::String)) - }) +fn is_docstring(line: &LogicalLine) -> bool { + !line.tokens_trimmed().is_empty() + && line + .tokens_trimmed() + .iter() + .all(|token| matches!(token.kind(), TokenKind::String)) } /// Returns `true` if the token is Async, Class or Def @@ -576,7 +574,7 @@ pub(crate) fn blank_lines( tracked_vars.is_not_first_logical_line = true; } - tracked_vars.follows_docstring = is_docstring(Some(line)); + tracked_vars.follows_docstring = is_docstring(line); tracked_vars.last_non_comment_line_end = line .tokens() From 60311c3f11b2439cda73953a9e3c327af479a224 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 28 Nov 2023 16:19:07 +0900 Subject: [PATCH 046/122] Use an enum to keep track of what the line is following. --- .../rules/logical_lines/blank_lines.rs | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 0600b9cf1df46..0d346e0347c19 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -18,9 +18,7 @@ use super::LogicalLine; #[derive(Debug, Default)] #[allow(clippy::struct_excessive_bools)] pub(crate) struct BlankLinesTrackingVars { - follows_decorator: bool, - follows_def: bool, - follows_docstring: bool, + follows: Follows, fn_status: Status, class_status: Status, /// First line that is not a comment. @@ -36,6 +34,14 @@ const BLANK_LINES_TOP_LEVEL: u32 = 2; /// Number of blank lines around methods and nested classes and functions. const BLANK_LINES_METHOD_LEVEL: u32 = 1; +#[derive(Copy, Clone, Debug, Default)] +enum Follows { + #[default] + Other, + Decorator, + Def, + Docstring, +} #[derive(Copy, Clone, Debug, Default)] enum Status { /// Stores the indent level where the nesting started. @@ -386,13 +392,12 @@ pub(crate) fn blank_lines( tracked_vars.class_status = Status::Outside; } } - let first_token = match line + let Some(first_token) = line .tokens() .iter() .find(|token| !matches!(token.kind, TokenKind::Indent | TokenKind::Dedent)) - { - Some(token) => token, - None => return, + else { + return; }; // Don't expect blank lines before the first non comment line. @@ -402,11 +407,11 @@ pub(crate) fn blank_lines( && first_token.kind() == TokenKind::Def && matches!(tracked_vars.class_status, Status::Inside(_)) // The class/parent method's docstring can directly precede the def. - && !tracked_vars.follows_docstring + && !matches!(tracked_vars.follows, Follows::Docstring) // Do not trigger when the def follows an if/while/etc... && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !tracked_vars.follows_decorator + && !matches!(tracked_vars.follows, Follows::Decorator) { // E301 let mut diagnostic = Diagnostic::new( @@ -423,9 +428,9 @@ pub(crate) fn blank_lines( if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !tracked_vars.follows_decorator + && !matches!(tracked_vars.follows, Follows::Decorator) // Allow groups of one-liners. - && !(tracked_vars.follows_def + && !(matches!(tracked_vars.follows, Follows::Def) && line .tokens_trimmed() .last() @@ -472,7 +477,8 @@ pub(crate) fn blank_lines( context.push_diagnostic(diagnostic); } - if tracked_vars.follows_decorator && line.line.preceding_blank_lines > 0 { + if matches!(tracked_vars.follows, Follows::Decorator) && line.line.preceding_blank_lines > 0 + { // E304 let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, first_token.range); @@ -513,15 +519,15 @@ pub(crate) fn blank_lines( if line.line.preceding_blank_lines == 0 // Only apply to nested functions. && matches!(tracked_vars.fn_status, Status::Inside(_)) - && is_top_level_token_or_decorator(first_token.kind) + && is_top_level_token_or_decorator(first_token.kind) // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !tracked_vars.follows_decorator + && !matches!(tracked_vars.follows, Follows::Decorator) // The class's docstring can directly precede the first function. - && !tracked_vars.follows_docstring + && !matches!(tracked_vars.follows, Follows::Docstring) // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) // Allow groups of one-liners. - && !(tracked_vars.follows_def + && !(matches!(tracked_vars.follows, Follows::Def) && line .tokens_trimmed() .last() @@ -548,24 +554,20 @@ pub(crate) fn blank_lines( if matches!(tracked_vars.class_status, Status::Outside) { tracked_vars.class_status = Status::Inside(indent_level + indent_size); } - tracked_vars.follows_decorator = false; - tracked_vars.follows_def = false; + tracked_vars.follows = Follows::Other; } TokenKind::At => { - tracked_vars.follows_decorator = true; - tracked_vars.follows_def = false; + tracked_vars.follows = Follows::Decorator; } TokenKind::Def | TokenKind::Async => { if matches!(tracked_vars.fn_status, Status::Outside) { tracked_vars.fn_status = Status::Inside(indent_level + indent_size); } - tracked_vars.follows_def = true; - tracked_vars.follows_decorator = false; + tracked_vars.follows = Follows::Def; } TokenKind::Comment => {} _ => { - tracked_vars.follows_decorator = false; - tracked_vars.follows_def = false; + tracked_vars.follows = Follows::Other; } } @@ -574,7 +576,9 @@ pub(crate) fn blank_lines( tracked_vars.is_not_first_logical_line = true; } - tracked_vars.follows_docstring = is_docstring(line); + if is_docstring(line) { + tracked_vars.follows = Follows::Docstring; + } tracked_vars.last_non_comment_line_end = line .tokens() From dc2fe49ce2072821dd50eebd183f5a96892a5ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ho=C3=ABl=20Bagard?= <34478245+hoel-bagard@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:21:14 +0900 Subject: [PATCH 047/122] Update crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs Co-authored-by: Micha Reiser --- .../src/rules/pycodestyle/rules/logical_lines/blank_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 0d346e0347c19..2ab28acc1cc42 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -330,7 +330,7 @@ fn is_docstring(line: &LogicalLine) -> bool { /// Returns `true` if the token is Async, Class or Def fn is_top_level_token(token: Option) -> bool { - token.is_some_and(|token| matches!(token, TokenKind::Class | TokenKind::Def | TokenKind::Async)) + matches!(token, Some(TokenKind::Class | TokenKind::Def | TokenKind::Async)) } /// Returns `true` if the token is At, Async, Class or Def From 727a17bf2b274ce1fae0fbca623a6af9a9ee8b04 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 28 Nov 2023 16:28:36 +0900 Subject: [PATCH 048/122] BlankLinesTrackingVars -> BlankLinesChecker With BlankLinesChecker containing the method doing the checking. --- .../ruff_linter/src/checkers/logical_lines.rs | 13 +- .../rules/logical_lines/blank_lines.rs | 406 +++++++++--------- 2 files changed, 211 insertions(+), 208 deletions(-) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 683a6ce7b693c..90ffba5406c01 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -7,10 +7,10 @@ use ruff_text_size::{Ranged, TextRange}; use crate::registry::AsRule; use crate::rules::pycodestyle::rules::logical_lines::{ - blank_lines, extraneous_whitespace, indentation, missing_whitespace, - missing_whitespace_after_keyword, missing_whitespace_around_operator, space_after_comma, - space_around_operator, whitespace_around_keywords, whitespace_around_named_parameter_equals, - whitespace_before_comment, whitespace_before_parameters, BlankLinesTrackingVars, LogicalLines, + extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword, + missing_whitespace_around_operator, space_after_comma, space_around_operator, + whitespace_around_keywords, whitespace_around_named_parameter_equals, + whitespace_before_comment, whitespace_before_parameters, BlankLinesChecker, LogicalLines, TokenFlags, }; use crate::settings::LinterSettings; @@ -39,7 +39,7 @@ pub(crate) fn check_logical_lines( ) -> Vec { let mut context = LogicalLinesContext::new(settings); - let mut blank_lines_tracking_vars = BlankLinesTrackingVars::default(); + let mut blank_lines_checker = BlankLinesChecker::default(); let mut non_comment_prev_line = None; let mut prev_indent_level = None; let indent_char = stylist.indentation().as_char(); @@ -103,9 +103,8 @@ pub(crate) fn check_logical_lines( } } - blank_lines( + blank_lines_checker.check_line( &line, - &mut blank_lines_tracking_vars, prev_indent_level, indent_level, indent_size, diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 2ab28acc1cc42..a4e453d764cf4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -17,7 +17,7 @@ use super::LogicalLine; /// Contains variables used for the linting of blank lines. #[derive(Debug, Default)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct BlankLinesTrackingVars { +pub(crate) struct BlankLinesChecker { follows: Follows, fn_status: Status, class_status: Status, @@ -330,7 +330,10 @@ fn is_docstring(line: &LogicalLine) -> bool { /// Returns `true` if the token is Async, Class or Def fn is_top_level_token(token: Option) -> bool { - matches!(token, Some(TokenKind::Class | TokenKind::Def | TokenKind::Async)) + matches!( + token, + Some(TokenKind::Class | TokenKind::Def | TokenKind::Async) + ) } /// Returns `true` if the token is At, Async, Class or Def @@ -341,96 +344,97 @@ fn is_top_level_token_or_decorator(token: TokenKind) -> bool { ) } -/// E301, E302, E303, E304, E305, E306 -#[allow(clippy::too_many_arguments)] -pub(crate) fn blank_lines( - line: &LogicalLine, - tracked_vars: &mut BlankLinesTrackingVars, - prev_indent_level: Option, - indent_level: usize, - indent_size: usize, - locator: &Locator, - stylist: &Stylist, - context: &mut LogicalLinesContext, -) { - let line_is_comment_only = line.is_comment_only(); - - if let Status::Inside(nesting_indent) = tracked_vars.class_status { - if indent_level < nesting_indent { - if line_is_comment_only { - tracked_vars.class_status = Status::CommentAfter(nesting_indent); - } else { - tracked_vars.class_status = Status::Outside; +impl BlankLinesChecker { + /// E301, E302, E303, E304, E305, E306 + #[allow(clippy::too_many_arguments)] + pub(crate) fn check_line( + &mut self, + line: &LogicalLine, + prev_indent_level: Option, + indent_level: usize, + indent_size: usize, + locator: &Locator, + stylist: &Stylist, + context: &mut LogicalLinesContext, + ) { + let line_is_comment_only = line.is_comment_only(); + + if let Status::Inside(nesting_indent) = self.class_status { + if indent_level < nesting_indent { + if line_is_comment_only { + self.class_status = Status::CommentAfter(nesting_indent); + } else { + self.class_status = Status::Outside; + } } } - } - if let Status::Inside(nesting_indent) = tracked_vars.fn_status { - if indent_level < nesting_indent { - if line_is_comment_only { - tracked_vars.fn_status = Status::CommentAfter(nesting_indent); - } else { - tracked_vars.fn_status = Status::Outside; + if let Status::Inside(nesting_indent) = self.fn_status { + if indent_level < nesting_indent { + if line_is_comment_only { + self.fn_status = Status::CommentAfter(nesting_indent); + } else { + self.fn_status = Status::Outside; + } } } - } - // A comment can be de-indented while still being in a class/function, in that case - // we need to revert the variables. - if !line_is_comment_only { - if let Status::CommentAfter(indent) = tracked_vars.fn_status { - if indent_level >= indent { - tracked_vars.fn_status = Status::Inside(indent); + // A comment can be de-indented while still being in a class/function, in that case + // we need to revert the variables. + if !line_is_comment_only { + if let Status::CommentAfter(indent) = self.fn_status { + if indent_level >= indent { + self.fn_status = Status::Inside(indent); + } + self.fn_status = Status::Outside; } - tracked_vars.fn_status = Status::Outside; - } - if let Status::CommentAfter(indent) = tracked_vars.class_status { - if indent_level >= indent { - tracked_vars.class_status = Status::Inside(indent); + if let Status::CommentAfter(indent) = self.class_status { + if indent_level >= indent { + self.class_status = Status::Inside(indent); + } + self.class_status = Status::Outside; } - tracked_vars.class_status = Status::Outside; } - } - let Some(first_token) = line - .tokens() - .iter() - .find(|token| !matches!(token.kind, TokenKind::Indent | TokenKind::Dedent)) - else { - return; - }; - - // Don't expect blank lines before the first non comment line. - if tracked_vars.is_not_first_logical_line { - if line.line.preceding_blank_lines == 0 + let Some(first_token) = line + .tokens() + .iter() + .find(|token| !matches!(token.kind, TokenKind::Indent | TokenKind::Dedent)) + else { + return; + }; + + // Don't expect blank lines before the first non comment line. + if self.is_not_first_logical_line { + if line.line.preceding_blank_lines == 0 // Only applies to methods. && first_token.kind() == TokenKind::Def - && matches!(tracked_vars.class_status, Status::Inside(_)) + && matches!(self.class_status, Status::Inside(_)) // The class/parent method's docstring can directly precede the def. - && !matches!(tracked_vars.follows, Follows::Docstring) + && !matches!(self.follows, Follows::Docstring) // Do not trigger when the def follows an if/while/etc... && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !matches!(tracked_vars.follows, Follows::Decorator) - { - // E301 - let mut diagnostic = Diagnostic::new( - BlankLineBetweenMethods(line.line.preceding_blank_lines), - first_token.range, - ); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), - locator.line_start(tracked_vars.last_non_comment_line_end), - ))); - - context.push_diagnostic(diagnostic); - } + && !matches!(self.follows, Follows::Decorator) + { + // E301 + let mut diagnostic = Diagnostic::new( + BlankLineBetweenMethods(line.line.preceding_blank_lines), + first_token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(self.last_non_comment_line_end), + ))); + + context.push_diagnostic(diagnostic); + } - if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !matches!(tracked_vars.follows, Follows::Decorator) + && !matches!(self.follows, Follows::Decorator) // Allow groups of one-liners. - && !(matches!(tracked_vars.follows, Follows::Def) + && !(matches!(self.follows, Follows::Def) && line .tokens_trimmed() .last() @@ -440,160 +444,160 @@ pub(crate) fn blank_lines( && indent_level == 0 // Only apply to functions or classes. && is_top_level_token_or_decorator(first_token.kind) - { - // E302 - let mut diagnostic = Diagnostic::new( - BlankLinesTopLevel(line.line.preceding_blank_lines), - first_token.range, - ); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .as_str() - .to_string() - .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), - locator.line_start(tracked_vars.last_non_comment_line_end), - ))); - - context.push_diagnostic(diagnostic); - } + { + // E302 + let mut diagnostic = Diagnostic::new( + BlankLinesTopLevel(line.line.preceding_blank_lines), + first_token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .as_str() + .to_string() + .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), + locator.line_start(self.last_non_comment_line_end), + ))); + + context.push_diagnostic(diagnostic); + } - if line.line.blank_lines > BLANK_LINES_TOP_LEVEL - || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) - { - // E303 - let mut diagnostic = - Diagnostic::new(TooManyBlankLines(line.line.blank_lines), first_token.range); - - let chars_to_remove = if indent_level > 0 { - line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL - } else { - line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL - }; - let end = locator.line_start(first_token.range.start()); - let start = end - TextSize::new(chars_to_remove); - diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); - - context.push_diagnostic(diagnostic); - } + if line.line.blank_lines > BLANK_LINES_TOP_LEVEL + || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) + { + // E303 + let mut diagnostic = + Diagnostic::new(TooManyBlankLines(line.line.blank_lines), first_token.range); + + let chars_to_remove = if indent_level > 0 { + line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL + } else { + line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL + }; + let end = locator.line_start(first_token.range.start()); + let start = end - TextSize::new(chars_to_remove); + diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); + + context.push_diagnostic(diagnostic); + } - if matches!(tracked_vars.follows, Follows::Decorator) && line.line.preceding_blank_lines > 0 - { - // E304 - let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, first_token.range); + if matches!(self.follows, Follows::Decorator) && line.line.preceding_blank_lines > 0 { + // E304 + let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, first_token.range); - let range = first_token.range; - diagnostic.set_fix(Fix::safe_edit(Edit::deletion( - locator.line_start(range.start()) - - TextSize::new(line.line.preceding_blank_characters), - locator.line_start(range.start()), - ))); + let range = first_token.range; + diagnostic.set_fix(Fix::safe_edit(Edit::deletion( + locator.line_start(range.start()) + - TextSize::new(line.line.preceding_blank_characters), + locator.line_start(range.start()), + ))); - context.push_diagnostic(diagnostic); - } + context.push_diagnostic(diagnostic); + } - if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL - && is_top_level_token(tracked_vars.previous_unindented_token) - && indent_level == 0 - && !line_is_comment_only - && !is_top_level_token_or_decorator(first_token.kind) - { - // E305 - let mut diagnostic = Diagnostic::new( - BlankLinesAfterFunctionOrClass(line.line.blank_lines), - first_token.range, - ); - - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .as_str() - .to_string() - .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), - locator.line_start(first_token.range.start()), - ))); - - context.push_diagnostic(diagnostic); - } + if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + && is_top_level_token(self.previous_unindented_token) + && indent_level == 0 + && !line_is_comment_only + && !is_top_level_token_or_decorator(first_token.kind) + { + // E305 + let mut diagnostic = Diagnostic::new( + BlankLinesAfterFunctionOrClass(line.line.blank_lines), + first_token.range, + ); + + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .as_str() + .to_string() + .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), + locator.line_start(first_token.range.start()), + ))); + + context.push_diagnostic(diagnostic); + } - if line.line.preceding_blank_lines == 0 + if line.line.preceding_blank_lines == 0 // Only apply to nested functions. - && matches!(tracked_vars.fn_status, Status::Inside(_)) + && matches!(self.fn_status, Status::Inside(_)) && is_top_level_token_or_decorator(first_token.kind) // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !matches!(tracked_vars.follows, Follows::Decorator) + && !matches!(self.follows, Follows::Decorator) // The class's docstring can directly precede the first function. - && !matches!(tracked_vars.follows, Follows::Docstring) + && !matches!(self.follows, Follows::Docstring) // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) // Allow groups of one-liners. - && !(matches!(tracked_vars.follows, Follows::Def) + && !(matches!(self.follows, Follows::Def) && line .tokens_trimmed() .last() .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) ) - { - // E306 - let mut diagnostic = Diagnostic::new( - BlankLinesBeforeNestedDefinition(line.line.blank_lines), - first_token.range, - ); - - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), - locator.line_start(first_token.range.start()), - ))); - - context.push_diagnostic(diagnostic); + { + // E306 + let mut diagnostic = Diagnostic::new( + BlankLinesBeforeNestedDefinition(line.line.blank_lines), + first_token.range, + ); + + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(first_token.range.start()), + ))); + + context.push_diagnostic(diagnostic); + } } - } - match first_token.kind() { - TokenKind::Class => { - if matches!(tracked_vars.class_status, Status::Outside) { - tracked_vars.class_status = Status::Inside(indent_level + indent_size); + match first_token.kind() { + TokenKind::Class => { + if matches!(self.class_status, Status::Outside) { + self.class_status = Status::Inside(indent_level + indent_size); + } + self.follows = Follows::Other; } - tracked_vars.follows = Follows::Other; - } - TokenKind::At => { - tracked_vars.follows = Follows::Decorator; - } - TokenKind::Def | TokenKind::Async => { - if matches!(tracked_vars.fn_status, Status::Outside) { - tracked_vars.fn_status = Status::Inside(indent_level + indent_size); + TokenKind::At => { + self.follows = Follows::Decorator; + } + TokenKind::Def | TokenKind::Async => { + if matches!(self.fn_status, Status::Outside) { + self.fn_status = Status::Inside(indent_level + indent_size); + } + self.follows = Follows::Def; + } + TokenKind::Comment => {} + _ => { + self.follows = Follows::Other; } - tracked_vars.follows = Follows::Def; - } - TokenKind::Comment => {} - _ => { - tracked_vars.follows = Follows::Other; } - } - if !line_is_comment_only { - if !tracked_vars.is_not_first_logical_line { - tracked_vars.is_not_first_logical_line = true; - } + if !line_is_comment_only { + if !self.is_not_first_logical_line { + self.is_not_first_logical_line = true; + } - if is_docstring(line) { - tracked_vars.follows = Follows::Docstring; - } + if is_docstring(line) { + self.follows = Follows::Docstring; + } - tracked_vars.last_non_comment_line_end = line - .tokens() - .last() - .expect("Line to contain at least one token.") - .range - .end(); - - if indent_level == 0 && !line.tokens_trimmed().is_empty() { - tracked_vars.previous_unindented_token = Some( - line.tokens_trimmed() - .first() - .expect("Previously checked.") - .kind, - ); + self.last_non_comment_line_end = line + .tokens() + .last() + .expect("Line to contain at least one token.") + .range + .end(); + + if indent_level == 0 && !line.tokens_trimmed().is_empty() { + self.previous_unindented_token = Some( + line.tokens_trimmed() + .first() + .expect("Previously checked.") + .kind, + ); + } } } } From 0635407ac258aa07e6dae4f50bc349860567d1fa Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Thu, 14 Dec 2023 19:18:57 +0900 Subject: [PATCH 049/122] WIP --- .../ruff_linter/src/checkers/logical_lines.rs | 14 +- .../rules/logical_lines/blank_lines.rs | 592 +++++++++++------- 2 files changed, 357 insertions(+), 249 deletions(-) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 90ffba5406c01..39c120ac92086 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -16,7 +16,7 @@ use crate::rules::pycodestyle::rules::logical_lines::{ use crate::settings::LinterSettings; /// Return the amount of indentation, expanding tabs to the next multiple of 8. -fn expand_indent(line: &str) -> usize { +pub(crate) fn expand_indent(line: &str) -> usize { let line = line.trim_end_matches(['\n', '\r']); let mut indent = 0; @@ -44,6 +44,8 @@ pub(crate) fn check_logical_lines( let mut prev_indent_level = None; let indent_char = stylist.indentation().as_char(); + blank_lines_checker.check_content(tokens, 4, locator, stylist, &mut context); + for line in &LogicalLines::from_tokens(tokens, locator) { if line.flags().contains(TokenFlags::OPERATOR) { space_around_operator(&line, &mut context); @@ -103,16 +105,6 @@ pub(crate) fn check_logical_lines( } } - blank_lines_checker.check_line( - &line, - prev_indent_level, - indent_level, - indent_size, - locator, - stylist, - &mut context, - ); - if !line.is_comment_only() { non_comment_prev_line = Some(line); prev_indent_level = Some(indent_level); diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index a4e453d764cf4..c673b354d2fbc 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -2,14 +2,21 @@ use ruff_diagnostics::AlwaysFixableViolation; use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Edit; use ruff_diagnostics::Fix; +use std::iter::Flatten; +use std::slice::Iter; use ruff_macros::{derive_message_formats, violation}; use ruff_python_codegen::Stylist; +use ruff_python_parser::lexer::LexResult; +use ruff_python_parser::lexer::LexicalError; +use ruff_python_parser::Tok; use ruff_python_parser::TokenKind; use ruff_source_file::Locator; +use ruff_text_size::TextRange; use ruff_text_size::TextSize; +use crate::checkers::logical_lines::expand_indent; use crate::checkers::logical_lines::LogicalLinesContext; use super::LogicalLine; @@ -27,6 +34,7 @@ pub(crate) struct BlankLinesChecker { /// to the second line instead of the first. last_non_comment_line_end: TextSize, previous_unindented_token: Option, + prev_indent_level: Option, } /// Number of blank lines around top level classes and functions. @@ -344,260 +352,368 @@ fn is_top_level_token_or_decorator(token: TokenKind) -> bool { ) } -impl BlankLinesChecker { - /// E301, E302, E303, E304, E305, E306 - #[allow(clippy::too_many_arguments)] - pub(crate) fn check_line( - &mut self, - line: &LogicalLine, - prev_indent_level: Option, - indent_level: usize, - indent_size: usize, - locator: &Locator, - stylist: &Stylist, - context: &mut LogicalLinesContext, - ) { - let line_is_comment_only = line.is_comment_only(); +struct LinePreprocessor<'a> { + tokens: Flatten>>, + locator: &'a Locator<'a>, +} - if let Status::Inside(nesting_indent) = self.class_status { - if indent_level < nesting_indent { - if line_is_comment_only { - self.class_status = Status::CommentAfter(nesting_indent); - } else { - self.class_status = Status::Outside; - } - } - } - - if let Status::Inside(nesting_indent) = self.fn_status { - if indent_level < nesting_indent { - if line_is_comment_only { - self.fn_status = Status::CommentAfter(nesting_indent); - } else { - self.fn_status = Status::Outside; - } - } +impl<'a> LinePreprocessor<'a> { + fn new(tokens: &'a [LexResult], locator: &'a Locator) -> LinePreprocessor<'a> { + LinePreprocessor { + tokens: tokens.into_iter().flatten(), + locator, } + } +} - // A comment can be de-indented while still being in a class/function, in that case - // we need to revert the variables. - if !line_is_comment_only { - if let Status::CommentAfter(indent) = self.fn_status { - if indent_level >= indent { - self.fn_status = Status::Inside(indent); - } - self.fn_status = Status::Outside; +impl<'a> Iterator for LinePreprocessor<'a> { + type Item = (Option, bool, usize); + + fn next(&mut self) -> Option<(Option, bool, usize)> { + let mut line_is_comment_only = true; + let mut first_token: Option = None; + let mut first_token_range = TextRange::new(0.into(), 0.into()); + let mut indent_level = 0; + + while let Some((token, range)) = self.tokens.next() { + dbg!(token); + // TODO: need to identify when a NonLogicalNewLine creates a new logical line (for example after a comment or on an empty line). + if first_token.is_none() && !matches!(token, Tok::Indent | Tok::Dedent | Tok::Newline) { + first_token = Some(token.clone()); + first_token_range = range.clone(); } - if let Status::CommentAfter(indent) = self.class_status { - if indent_level >= indent { - self.class_status = Status::Inside(indent); - } - self.class_status = Status::Outside; - } - } - let Some(first_token) = line - .tokens() - .iter() - .find(|token| !matches!(token.kind, TokenKind::Indent | TokenKind::Dedent)) - else { - return; - }; - - // Don't expect blank lines before the first non comment line. - if self.is_not_first_logical_line { - if line.line.preceding_blank_lines == 0 - // Only applies to methods. - && first_token.kind() == TokenKind::Def - && matches!(self.class_status, Status::Inside(_)) - // The class/parent method's docstring can directly precede the def. - && !matches!(self.follows, Follows::Docstring) - // Do not trigger when the def follows an if/while/etc... - && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) - // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !matches!(self.follows, Follows::Decorator) - { - // E301 - let mut diagnostic = Diagnostic::new( - BlankLineBetweenMethods(line.line.preceding_blank_lines), - first_token.range, - ); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), - locator.line_start(self.last_non_comment_line_end), - ))); - - context.push_diagnostic(diagnostic); + if !matches!(token, Tok::Comment(_)) { + line_is_comment_only = false; } - if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL - // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !matches!(self.follows, Follows::Decorator) - // Allow groups of one-liners. - && !(matches!(self.follows, Follows::Def) - && line - .tokens_trimmed() - .last() - .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) - ) - // Only trigger on non-indented classes and functions (for example functions within an if are ignored) - && indent_level == 0 - // Only apply to functions or classes. - && is_top_level_token_or_decorator(first_token.kind) - { - // E302 - let mut diagnostic = Diagnostic::new( - BlankLinesTopLevel(line.line.preceding_blank_lines), - first_token.range, - ); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .as_str() - .to_string() - .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), - locator.line_start(self.last_non_comment_line_end), - ))); - - context.push_diagnostic(diagnostic); - } - - if line.line.blank_lines > BLANK_LINES_TOP_LEVEL - || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) - { - // E303 - let mut diagnostic = - Diagnostic::new(TooManyBlankLines(line.line.blank_lines), first_token.range); - - let chars_to_remove = if indent_level > 0 { - line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL + if matches!(token, Tok::Newline) { + let range = if matches!(first_token, Some(Tok::Indent)) { + first_token_range } else { - line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL + TextRange::new( + self.locator.line_start(first_token_range.start()), + first_token_range.start(), + ) }; - let end = locator.line_start(first_token.range.start()); - let start = end - TextSize::new(chars_to_remove); - diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); - - context.push_diagnostic(diagnostic); - } - - if matches!(self.follows, Follows::Decorator) && line.line.preceding_blank_lines > 0 { - // E304 - let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, first_token.range); - - let range = first_token.range; - diagnostic.set_fix(Fix::safe_edit(Edit::deletion( - locator.line_start(range.start()) - - TextSize::new(line.line.preceding_blank_characters), - locator.line_start(range.start()), - ))); - - context.push_diagnostic(diagnostic); - } - - if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL - && is_top_level_token(self.previous_unindented_token) - && indent_level == 0 - && !line_is_comment_only - && !is_top_level_token_or_decorator(first_token.kind) - { - // E305 - let mut diagnostic = Diagnostic::new( - BlankLinesAfterFunctionOrClass(line.line.blank_lines), - first_token.range, - ); - - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .as_str() - .to_string() - .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), - locator.line_start(first_token.range.start()), - ))); - - context.push_diagnostic(diagnostic); - } - - if line.line.preceding_blank_lines == 0 - // Only apply to nested functions. - && matches!(self.fn_status, Status::Inside(_)) - && is_top_level_token_or_decorator(first_token.kind) - // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !matches!(self.follows, Follows::Decorator) - // The class's docstring can directly precede the first function. - && !matches!(self.follows, Follows::Docstring) - // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). - && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) - // Allow groups of one-liners. - && !(matches!(self.follows, Follows::Def) - && line - .tokens_trimmed() - .last() - .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) - ) - { - // E306 - let mut diagnostic = Diagnostic::new( - BlankLinesBeforeNestedDefinition(line.line.blank_lines), - first_token.range, - ); - - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), - locator.line_start(first_token.range.start()), - ))); - - context.push_diagnostic(diagnostic); + indent_level = expand_indent(self.locator.slice(range)); + break; } } - match first_token.kind() { - TokenKind::Class => { - if matches!(self.class_status, Status::Outside) { - self.class_status = Status::Inside(indent_level + indent_size); - } - self.follows = Follows::Other; - } - TokenKind::At => { - self.follows = Follows::Decorator; - } - TokenKind::Def | TokenKind::Async => { - if matches!(self.fn_status, Status::Outside) { - self.fn_status = Status::Inside(indent_level + indent_size); - } - self.follows = Follows::Def; - } - TokenKind::Comment => {} - _ => { - self.follows = Follows::Other; - } + if first_token.is_none() { + line_is_comment_only = false; + return None; } - if !line_is_comment_only { - if !self.is_not_first_logical_line { - self.is_not_first_logical_line = true; - } + return Some((first_token, line_is_comment_only, indent_level)); + } +} - if is_docstring(line) { - self.follows = Follows::Docstring; - } +impl BlankLinesChecker { + /// E301, E302, E303, E304, E305, E306 + #[allow(clippy::too_many_arguments)] + pub(crate) fn check_content( + &mut self, + tokens: &[LexResult], + // line: &LogicalLine, + // prev_indent_level: Option, + // indent_level: usize, + indent_size: usize, + locator: &Locator, + stylist: &Stylist, + context: &mut LogicalLinesContext, + ) { + // dbg!(tokens); - self.last_non_comment_line_end = line - .tokens() - .last() - .expect("Line to contain at least one token.") - .range - .end(); - - if indent_level == 0 && !line.tokens_trimmed().is_empty() { - self.previous_unindented_token = Some( - line.tokens_trimmed() - .first() - .expect("Previously checked.") - .kind, - ); - } + let line_preprocessor = LinePreprocessor::new(tokens, locator); + + for (first_token, line_is_comment_only, indent_level) in line_preprocessor { + dbg!(first_token, line_is_comment_only, indent_level); } + + // blank_lines: self.current_blank_lines, + // preceding_blank_lines: self.previous_blank_lines, + // preceding_blank_characters: self.current_blank_characters, + + // let end = self.tokens.len() as u32; + // if self.current_line.tokens_start < end { + // let is_empty = self.tokens[self.current_line.tokens_start as usize..end as usize] + // .iter() + // .all(|token| token.kind.is_newline()); + // if is_empty { + // self.current_blank_lines += 1; + // self.current_blank_characters += end - self.current_line.tokens_start; + // } else { + // if self.previous_blank_lines < self.current_blank_lines { + // self.previous_blank_lines = self.current_blank_lines; + // } + // self.lines.push(Line { + // flags: self.current_line.flags, + // blank_lines: self.current_blank_lines, + // preceding_blank_lines: self.previous_blank_lines, + // preceding_blank_characters: self.current_blank_characters, + // tokens_start: self.current_line.tokens_start, + // tokens_end: end, + // }); + // if self.current_line.flags != TokenFlags::COMMENT { + // self.previous_blank_lines = 0; + // } + // self.current_blank_lines = 0; + // self.current_blank_characters = 0; + // } + + // self.current_line = CurrentLine { + // flags: TokenFlags::default(), + // tokens_start: end, + // } + return; } + + // pub(crate) fn check_line( + // &mut self, + // first_token: Tok, + // first_token_range: TextRange, + // line_is_comment_only: bool, + // prev_indent_level: Option, + // indent_level: usize, + // indent_size: usize, + // locator: &Locator, + // stylist: &Stylist, + // context: &mut LogicalLinesContext, + // ) { + // if let Status::Inside(nesting_indent) = self.class_status { + // if indent_level < nesting_indent { + // if line_is_comment_only { + // self.class_status = Status::CommentAfter(nesting_indent); + // } else { + // self.class_status = Status::Outside; + // } + // } + // } + + // if let Status::Inside(nesting_indent) = self.fn_status { + // if indent_level < nesting_indent { + // if line_is_comment_only { + // self.fn_status = Status::CommentAfter(nesting_indent); + // } else { + // self.fn_status = Status::Outside; + // } + // } + // } + + // // A comment can be de-indented while still being in a class/function, in that case + // // we need to revert the variables. + // if !line_is_comment_only { + // if let Status::CommentAfter(indent) = self.fn_status { + // if indent_level >= indent { + // self.fn_status = Status::Inside(indent); + // } + // self.fn_status = Status::Outside; + // } + + // if let Status::CommentAfter(indent) = self.class_status { + // if indent_level >= indent { + // self.class_status = Status::Inside(indent); + // } + // self.class_status = Status::Outside; + // } + // } + + // // Don't expect blank lines before the first non comment line. + // if self.is_not_first_logical_line { + // if line.line.preceding_blank_lines == 0 + // // Only applies to methods. + // && first_token.kind() == TokenKind::Def + // && matches!(self.class_status, Status::Inside(_)) + // // The class/parent method's docstring can directly precede the def. + // && !matches!(self.follows, Follows::Docstring) + // // Do not trigger when the def follows an if/while/etc... + // && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) + // // Allow following a decorator (if there is an error it will be triggered on the first decorator). + // && !matches!(self.follows, Follows::Decorator) + // { + // // E301 + // let mut diagnostic = Diagnostic::new( + // BlankLineBetweenMethods(line.line.preceding_blank_lines), + // first_token.range, + // ); + // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + // stylist.line_ending().as_str().to_string(), + // locator.line_start(self.last_non_comment_line_end), + // ))); + + // context.push_diagnostic(diagnostic); + // } + + // if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + // // Allow following a decorator (if there is an error it will be triggered on the first decorator). + // && !matches!(self.follows, Follows::Decorator) + // // Allow groups of one-liners. + // && !(matches!(self.follows, Follows::Def) + // && line + // .tokens_trimmed() + // .last() + // .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + // ) + // // Only trigger on non-indented classes and functions (for example functions within an if are ignored) + // && indent_level == 0 + // // Only apply to functions or classes. + // && is_top_level_token_or_decorator(first_token.kind) + // { + // // E302 + // let mut diagnostic = Diagnostic::new( + // BlankLinesTopLevel(line.line.preceding_blank_lines), + // first_token.range, + // ); + // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + // stylist + // .line_ending() + // .as_str() + // .to_string() + // .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), + // locator.line_start(self.last_non_comment_line_end), + // ))); + + // context.push_diagnostic(diagnostic); + // } + + // if line.line.blank_lines > BLANK_LINES_TOP_LEVEL + // || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) + // { + // // E303 + // let mut diagnostic = + // Diagnostic::new(TooManyBlankLines(line.line.blank_lines), first_token.range); + + // let chars_to_remove = if indent_level > 0 { + // line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL + // } else { + // line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL + // }; + // let end = locator.line_start(first_token.range.start()); + // let start = end - TextSize::new(chars_to_remove); + // diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); + + // context.push_diagnostic(diagnostic); + // } + + // if matches!(self.follows, Follows::Decorator) && line.line.preceding_blank_lines > 0 { + // // E304 + // let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, first_token.range); + + // let range = first_token.range; + // diagnostic.set_fix(Fix::safe_edit(Edit::deletion( + // locator.line_start(range.start()) + // - TextSize::new(line.line.preceding_blank_characters), + // locator.line_start(range.start()), + // ))); + + // context.push_diagnostic(diagnostic); + // } + + // if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + // && is_top_level_token(self.previous_unindented_token) + // && indent_level == 0 + // && !line_is_comment_only + // && !is_top_level_token_or_decorator(first_token.kind) + // { + // // E305 + // let mut diagnostic = Diagnostic::new( + // BlankLinesAfterFunctionOrClass(line.line.blank_lines), + // first_token.range, + // ); + + // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + // stylist + // .line_ending() + // .as_str() + // .to_string() + // .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), + // locator.line_start(first_token.range.start()), + // ))); + + // context.push_diagnostic(diagnostic); + // } + + // if line.line.preceding_blank_lines == 0 + // // Only apply to nested functions. + // && matches!(self.fn_status, Status::Inside(_)) + // && is_top_level_token_or_decorator(first_token.kind) + // // Allow following a decorator (if there is an error it will be triggered on the first decorator). + // && !matches!(self.follows, Follows::Decorator) + // // The class's docstring can directly precede the first function. + // && !matches!(self.follows, Follows::Docstring) + // // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). + // && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) + // // Allow groups of one-liners. + // && !(matches!(self.follows, Follows::Def) + // && line + // .tokens_trimmed() + // .last() + // .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + // ) + // { + // // E306 + // let mut diagnostic = Diagnostic::new( + // BlankLinesBeforeNestedDefinition(line.line.blank_lines), + // first_token.range, + // ); + + // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + // stylist.line_ending().as_str().to_string(), + // locator.line_start(first_token.range.start()), + // ))); + + // context.push_diagnostic(diagnostic); + // } + // } + + // match first_token.kind() { + // TokenKind::Class => { + // if matches!(self.class_status, Status::Outside) { + // self.class_status = Status::Inside(indent_level + indent_size); + // } + // self.follows = Follows::Other; + // } + // TokenKind::At => { + // self.follows = Follows::Decorator; + // } + // TokenKind::Def | TokenKind::Async => { + // if matches!(self.fn_status, Status::Outside) { + // self.fn_status = Status::Inside(indent_level + indent_size); + // } + // self.follows = Follows::Def; + // } + // TokenKind::Comment => {} + // _ => { + // self.follows = Follows::Other; + // } + // } + + // if !line_is_comment_only { + // if !self.is_not_first_logical_line { + // self.is_not_first_logical_line = true; + // } + + // if is_docstring(line) { + // self.follows = Follows::Docstring; + // } + + // self.last_non_comment_line_end = line + // .tokens() + // .last() + // .expect("Line to contain at least one token.") + // .range + // .end(); + + // if indent_level == 0 && !line.tokens_trimmed().is_empty() { + // self.previous_unindented_token = Some( + // line.tokens_trimmed() + // .first() + // .expect("Previously checked.") + // .kind, + // ); + // } + // } + // } } From 199257d5ba8186322441e7fe2cb061c8ac5ef51e Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 16 Dec 2023 11:39:42 +0900 Subject: [PATCH 050/122] Started building LogicalLine replacement. --- .../rules/logical_lines/blank_lines.rs | 639 +++++++++--------- 1 file changed, 331 insertions(+), 308 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index c673b354d2fbc..a80be49cbdd1b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -352,9 +352,26 @@ fn is_top_level_token_or_decorator(token: TokenKind) -> bool { ) } +#[derive(Debug)] +struct LogicalLineInfo { + first_token: Tok, + line_is_comment_only: bool, + indent_level: usize, + blank_lines: u32, + preceding_blank_lines: u32, + preceding_blank_characters: TextSize, + tokens_start: TextSize, +} + struct LinePreprocessor<'a> { tokens: Flatten>>, locator: &'a Locator<'a>, + /// Number of previous consecutive blank lines. + previous_blank_lines: u32, + /// Number of consecutive blank lines. + current_blank_lines: u32, + /// Number of blank characters in the blank lines (\n vs \r\n for example). + current_blank_characters: TextSize, } impl<'a> LinePreprocessor<'a> { @@ -362,22 +379,25 @@ impl<'a> LinePreprocessor<'a> { LinePreprocessor { tokens: tokens.into_iter().flatten(), locator, + previous_blank_lines: 0, + current_blank_lines: 0, + current_blank_characters: 0.into(), } } } impl<'a> Iterator for LinePreprocessor<'a> { - type Item = (Option, bool, usize); + type Item = LogicalLineInfo; - fn next(&mut self) -> Option<(Option, bool, usize)> { + fn next(&mut self) -> Option { let mut line_is_comment_only = true; let mut first_token: Option = None; let mut first_token_range = TextRange::new(0.into(), 0.into()); - let mut indent_level = 0; + let mut parens = 0u32; + + let logical_line: LogicalLineInfo; while let Some((token, range)) = self.tokens.next() { - dbg!(token); - // TODO: need to identify when a NonLogicalNewLine creates a new logical line (for example after a comment or on an empty line). if first_token.is_none() && !matches!(token, Tok::Indent | Tok::Dedent | Tok::Newline) { first_token = Some(token.clone()); first_token_range = range.clone(); @@ -387,26 +407,57 @@ impl<'a> Iterator for LinePreprocessor<'a> { line_is_comment_only = false; } - if matches!(token, Tok::Newline) { - let range = if matches!(first_token, Some(Tok::Indent)) { - first_token_range - } else { - TextRange::new( - self.locator.line_start(first_token_range.start()), - first_token_range.start(), - ) - }; - indent_level = expand_indent(self.locator.slice(range)); - break; + match token { + Tok::Lbrace | Tok::Lpar | Tok::Lsqb => { + parens = parens.saturating_add(1); + } + Tok::Rbrace | Tok::Rpar | Tok::Rsqb => { + parens = parens.saturating_sub(1); + } + Tok::Newline | Tok::NonLogicalNewline if parens == 0 => { + let range = if matches!(first_token, Some(Tok::Indent)) { + first_token_range + } else { + TextRange::new( + self.locator.line_start(first_token_range.start()), + first_token_range.start(), + ) + }; + let indent_level = expand_indent(self.locator.slice(range)); + + // Empty line + if matches!(first_token, Some(Tok::NonLogicalNewline)) { + self.current_blank_lines += 1; + self.current_blank_characters += range.end() - first_token_range.start(); + return self.next(); + } else { + if self.previous_blank_lines < self.current_blank_lines { + self.previous_blank_lines = self.current_blank_lines; + } + + logical_line = LogicalLineInfo { + first_token: first_token.clone().unwrap(), + line_is_comment_only, // TODO: Have an enum flag (to unify with docstring) ? + indent_level, + blank_lines: self.current_blank_lines, + preceding_blank_lines: self.previous_blank_lines, + preceding_blank_characters: self.current_blank_characters, + tokens_start: first_token_range.start(), + }; + + if line_is_comment_only { + self.previous_blank_lines = 0; + } + self.current_blank_lines = 0; + self.current_blank_characters = 0.into(); + return Some(logical_line); + } + } + _ => {} } } - if first_token.is_none() { - line_is_comment_only = false; - return None; - } - - return Some((first_token, line_is_comment_only, indent_level)); + return None; } } @@ -417,303 +468,275 @@ impl BlankLinesChecker { &mut self, tokens: &[LexResult], // line: &LogicalLine, - // prev_indent_level: Option, + // // indent_level: usize, indent_size: usize, locator: &Locator, stylist: &Stylist, context: &mut LogicalLinesContext, ) { - // dbg!(tokens); - + let mut prev_indent_level: Option = None; + let indent_size: usize = 4; let line_preprocessor = LinePreprocessor::new(tokens, locator); - for (first_token, line_is_comment_only, indent_level) in line_preprocessor { - dbg!(first_token, line_is_comment_only, indent_level); + for logical_line in line_preprocessor { + dbg!(logical_line); + self.check_line(logical_line, prev_indent_level, locator, stylist, context); + prev_indent_level = Some(logical_line.indent_level); } - // blank_lines: self.current_blank_lines, - // preceding_blank_lines: self.previous_blank_lines, - // preceding_blank_characters: self.current_blank_characters, - - // let end = self.tokens.len() as u32; - // if self.current_line.tokens_start < end { - // let is_empty = self.tokens[self.current_line.tokens_start as usize..end as usize] - // .iter() - // .all(|token| token.kind.is_newline()); - // if is_empty { - // self.current_blank_lines += 1; - // self.current_blank_characters += end - self.current_line.tokens_start; - // } else { - // if self.previous_blank_lines < self.current_blank_lines { - // self.previous_blank_lines = self.current_blank_lines; - // } - // self.lines.push(Line { - // flags: self.current_line.flags, - // blank_lines: self.current_blank_lines, - // preceding_blank_lines: self.previous_blank_lines, - // preceding_blank_characters: self.current_blank_characters, - // tokens_start: self.current_line.tokens_start, - // tokens_end: end, - // }); - // if self.current_line.flags != TokenFlags::COMMENT { - // self.previous_blank_lines = 0; - // } - // self.current_blank_lines = 0; - // self.current_blank_characters = 0; - // } - - // self.current_line = CurrentLine { - // flags: TokenFlags::default(), - // tokens_start: end, - // } + // TODO: have an is_docstring flag. + // TODO: Need to keep last token for: + // && !(matches!(self.follows, Follows::Def) + // && line + // .tokens_trimmed() + // .last() + // .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + // ) + return; } - // pub(crate) fn check_line( - // &mut self, - // first_token: Tok, - // first_token_range: TextRange, - // line_is_comment_only: bool, - // prev_indent_level: Option, - // indent_level: usize, - // indent_size: usize, - // locator: &Locator, - // stylist: &Stylist, - // context: &mut LogicalLinesContext, - // ) { - // if let Status::Inside(nesting_indent) = self.class_status { - // if indent_level < nesting_indent { - // if line_is_comment_only { - // self.class_status = Status::CommentAfter(nesting_indent); - // } else { - // self.class_status = Status::Outside; - // } - // } - // } - - // if let Status::Inside(nesting_indent) = self.fn_status { - // if indent_level < nesting_indent { - // if line_is_comment_only { - // self.fn_status = Status::CommentAfter(nesting_indent); - // } else { - // self.fn_status = Status::Outside; - // } - // } - // } - - // // A comment can be de-indented while still being in a class/function, in that case - // // we need to revert the variables. - // if !line_is_comment_only { - // if let Status::CommentAfter(indent) = self.fn_status { - // if indent_level >= indent { - // self.fn_status = Status::Inside(indent); - // } - // self.fn_status = Status::Outside; - // } - - // if let Status::CommentAfter(indent) = self.class_status { - // if indent_level >= indent { - // self.class_status = Status::Inside(indent); - // } - // self.class_status = Status::Outside; - // } - // } - - // // Don't expect blank lines before the first non comment line. - // if self.is_not_first_logical_line { - // if line.line.preceding_blank_lines == 0 - // // Only applies to methods. - // && first_token.kind() == TokenKind::Def - // && matches!(self.class_status, Status::Inside(_)) - // // The class/parent method's docstring can directly precede the def. - // && !matches!(self.follows, Follows::Docstring) - // // Do not trigger when the def follows an if/while/etc... - // && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) - // // Allow following a decorator (if there is an error it will be triggered on the first decorator). - // && !matches!(self.follows, Follows::Decorator) - // { - // // E301 - // let mut diagnostic = Diagnostic::new( - // BlankLineBetweenMethods(line.line.preceding_blank_lines), - // first_token.range, - // ); - // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - // stylist.line_ending().as_str().to_string(), - // locator.line_start(self.last_non_comment_line_end), - // ))); - - // context.push_diagnostic(diagnostic); - // } - - // if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL - // // Allow following a decorator (if there is an error it will be triggered on the first decorator). - // && !matches!(self.follows, Follows::Decorator) - // // Allow groups of one-liners. - // && !(matches!(self.follows, Follows::Def) - // && line - // .tokens_trimmed() - // .last() - // .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) - // ) - // // Only trigger on non-indented classes and functions (for example functions within an if are ignored) - // && indent_level == 0 - // // Only apply to functions or classes. - // && is_top_level_token_or_decorator(first_token.kind) - // { - // // E302 - // let mut diagnostic = Diagnostic::new( - // BlankLinesTopLevel(line.line.preceding_blank_lines), - // first_token.range, - // ); - // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - // stylist - // .line_ending() - // .as_str() - // .to_string() - // .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), - // locator.line_start(self.last_non_comment_line_end), - // ))); - - // context.push_diagnostic(diagnostic); - // } - - // if line.line.blank_lines > BLANK_LINES_TOP_LEVEL - // || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) - // { - // // E303 - // let mut diagnostic = - // Diagnostic::new(TooManyBlankLines(line.line.blank_lines), first_token.range); - - // let chars_to_remove = if indent_level > 0 { - // line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL - // } else { - // line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL - // }; - // let end = locator.line_start(first_token.range.start()); - // let start = end - TextSize::new(chars_to_remove); - // diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); - - // context.push_diagnostic(diagnostic); - // } - - // if matches!(self.follows, Follows::Decorator) && line.line.preceding_blank_lines > 0 { - // // E304 - // let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, first_token.range); - - // let range = first_token.range; - // diagnostic.set_fix(Fix::safe_edit(Edit::deletion( - // locator.line_start(range.start()) - // - TextSize::new(line.line.preceding_blank_characters), - // locator.line_start(range.start()), - // ))); - - // context.push_diagnostic(diagnostic); - // } - - // if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL - // && is_top_level_token(self.previous_unindented_token) - // && indent_level == 0 - // && !line_is_comment_only - // && !is_top_level_token_or_decorator(first_token.kind) - // { - // // E305 - // let mut diagnostic = Diagnostic::new( - // BlankLinesAfterFunctionOrClass(line.line.blank_lines), - // first_token.range, - // ); - - // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - // stylist - // .line_ending() - // .as_str() - // .to_string() - // .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), - // locator.line_start(first_token.range.start()), - // ))); - - // context.push_diagnostic(diagnostic); - // } - - // if line.line.preceding_blank_lines == 0 - // // Only apply to nested functions. - // && matches!(self.fn_status, Status::Inside(_)) - // && is_top_level_token_or_decorator(first_token.kind) - // // Allow following a decorator (if there is an error it will be triggered on the first decorator). - // && !matches!(self.follows, Follows::Decorator) - // // The class's docstring can directly precede the first function. - // && !matches!(self.follows, Follows::Docstring) - // // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). - // && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) - // // Allow groups of one-liners. - // && !(matches!(self.follows, Follows::Def) - // && line - // .tokens_trimmed() - // .last() - // .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) - // ) - // { - // // E306 - // let mut diagnostic = Diagnostic::new( - // BlankLinesBeforeNestedDefinition(line.line.blank_lines), - // first_token.range, - // ); - - // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - // stylist.line_ending().as_str().to_string(), - // locator.line_start(first_token.range.start()), - // ))); - - // context.push_diagnostic(diagnostic); - // } - // } - - // match first_token.kind() { - // TokenKind::Class => { - // if matches!(self.class_status, Status::Outside) { - // self.class_status = Status::Inside(indent_level + indent_size); - // } - // self.follows = Follows::Other; - // } - // TokenKind::At => { - // self.follows = Follows::Decorator; - // } - // TokenKind::Def | TokenKind::Async => { - // if matches!(self.fn_status, Status::Outside) { - // self.fn_status = Status::Inside(indent_level + indent_size); - // } - // self.follows = Follows::Def; - // } - // TokenKind::Comment => {} - // _ => { - // self.follows = Follows::Other; - // } - // } - - // if !line_is_comment_only { - // if !self.is_not_first_logical_line { - // self.is_not_first_logical_line = true; - // } - - // if is_docstring(line) { - // self.follows = Follows::Docstring; - // } - - // self.last_non_comment_line_end = line - // .tokens() - // .last() - // .expect("Line to contain at least one token.") - // .range - // .end(); - - // if indent_level == 0 && !line.tokens_trimmed().is_empty() { - // self.previous_unindented_token = Some( - // line.tokens_trimmed() - // .first() - // .expect("Previously checked.") - // .kind, - // ); - // } - // } - // } + pub(crate) fn check_line( + &mut self, + line: LogicalLineInfo, + prev_indent_level: Option, + locator: &Locator, + stylist: &Stylist, + context: &mut LogicalLinesContext, + ) { + if let Status::Inside(nesting_indent) = self.class_status { + if indent_level < nesting_indent { + if line_is_comment_only { + self.class_status = Status::CommentAfter(nesting_indent); + } else { + self.class_status = Status::Outside; + } + } + } + + if let Status::Inside(nesting_indent) = self.fn_status { + if indent_level < nesting_indent { + if line_is_comment_only { + self.fn_status = Status::CommentAfter(nesting_indent); + } else { + self.fn_status = Status::Outside; + } + } + } + + // A comment can be de-indented while still being in a class/function, in that case + // we need to revert the variables. + if !line_is_comment_only { + if let Status::CommentAfter(indent) = self.fn_status { + if indent_level >= indent { + self.fn_status = Status::Inside(indent); + } + self.fn_status = Status::Outside; + } + + if let Status::CommentAfter(indent) = self.class_status { + if indent_level >= indent { + self.class_status = Status::Inside(indent); + } + self.class_status = Status::Outside; + } + } + + // Don't expect blank lines before the first non comment line. + if self.is_not_first_logical_line { + if line.line.preceding_blank_lines == 0 + // Only applies to methods. + && first_token.kind() == TokenKind::Def + && matches!(self.class_status, Status::Inside(_)) + // The class/parent method's docstring can directly precede the def. + && !matches!(self.follows, Follows::Docstring) + // Do not trigger when the def follows an if/while/etc... + && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) + // Allow following a decorator (if there is an error it will be triggered on the first decorator). + && !matches!(self.follows, Follows::Decorator) + { + // E301 + let mut diagnostic = Diagnostic::new( + BlankLineBetweenMethods(line.line.preceding_blank_lines), + first_token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(self.last_non_comment_line_end), + ))); + + context.push_diagnostic(diagnostic); + } + + if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + // Allow following a decorator (if there is an error it will be triggered on the first decorator). + && !matches!(self.follows, Follows::Decorator) + // Allow groups of one-liners. + && !(matches!(self.follows, Follows::Def) + && line + .tokens_trimmed() + .last() + .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + ) + // Only trigger on non-indented classes and functions (for example functions within an if are ignored) + && indent_level == 0 + // Only apply to functions or classes. + && is_top_level_token_or_decorator(first_token.kind) + { + // E302 + let mut diagnostic = Diagnostic::new( + BlankLinesTopLevel(line.line.preceding_blank_lines), + first_token.range, + ); + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .as_str() + .to_string() + .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), + locator.line_start(self.last_non_comment_line_end), + ))); + + context.push_diagnostic(diagnostic); + } + + if line.line.blank_lines > BLANK_LINES_TOP_LEVEL + || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) + { + // E303 + let mut diagnostic = + Diagnostic::new(TooManyBlankLines(line.line.blank_lines), first_token.range); + + let chars_to_remove = if indent_level > 0 { + line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL + } else { + line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL + }; + let end = locator.line_start(first_token.range.start()); + let start = end - TextSize::new(chars_to_remove); + diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); + + context.push_diagnostic(diagnostic); + } + + if matches!(self.follows, Follows::Decorator) && line.line.preceding_blank_lines > 0 { + // E304 + let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, first_token.range); + + let range = first_token.range; + diagnostic.set_fix(Fix::safe_edit(Edit::deletion( + locator.line_start(range.start()) + - TextSize::new(line.line.preceding_blank_characters), + locator.line_start(range.start()), + ))); + + context.push_diagnostic(diagnostic); + } + + if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + && is_top_level_token(self.previous_unindented_token) + && indent_level == 0 + && !line_is_comment_only + && !is_top_level_token_or_decorator(first_token.kind) + { + // E305 + let mut diagnostic = Diagnostic::new( + BlankLinesAfterFunctionOrClass(line.line.blank_lines), + first_token.range, + ); + + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .as_str() + .to_string() + .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), + locator.line_start(first_token.range.start()), + ))); + + context.push_diagnostic(diagnostic); + } + + if line.line.preceding_blank_lines == 0 + // Only apply to nested functions. + && matches!(self.fn_status, Status::Inside(_)) + && is_top_level_token_or_decorator(first_token.kind) + // Allow following a decorator (if there is an error it will be triggered on the first decorator). + && !matches!(self.follows, Follows::Decorator) + // The class's docstring can directly precede the first function. + && !matches!(self.follows, Follows::Docstring) + // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). + && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) + // Allow groups of one-liners. + && !(matches!(self.follows, Follows::Def) + && line + .tokens_trimmed() + .last() + .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) + ) + { + // E306 + let mut diagnostic = Diagnostic::new( + BlankLinesBeforeNestedDefinition(line.line.blank_lines), + first_token.range, + ); + + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().as_str().to_string(), + locator.line_start(first_token.range.start()), + ))); + + context.push_diagnostic(diagnostic); + } + } + + match first_token.kind() { + TokenKind::Class => { + if matches!(self.class_status, Status::Outside) { + self.class_status = Status::Inside(indent_level + indent_size); + } + self.follows = Follows::Other; + } + TokenKind::At => { + self.follows = Follows::Decorator; + } + TokenKind::Def | TokenKind::Async => { + if matches!(self.fn_status, Status::Outside) { + self.fn_status = Status::Inside(indent_level + indent_size); + } + self.follows = Follows::Def; + } + TokenKind::Comment => {} + _ => { + self.follows = Follows::Other; + } + } + + if !line_is_comment_only { + if !self.is_not_first_logical_line { + self.is_not_first_logical_line = true; + } + + if is_docstring(line) { + self.follows = Follows::Docstring; + } + + self.last_non_comment_line_end = line + .tokens() + .last() + .expect("Line to contain at least one token.") + .range + .end(); + + if indent_level == 0 && !line.tokens_trimmed().is_empty() { + self.previous_unindented_token = Some( + line.tokens_trimmed() + .first() + .expect("Previously checked.") + .kind, + ); + } + } + } } From 2002a6bd7010867b6f1d9af87992ae9bab3fb317 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 16 Dec 2023 15:38:40 +0900 Subject: [PATCH 051/122] Mostly finished renaming variable. --- .../ruff_linter/src/checkers/logical_lines.rs | 2 +- .../rules/logical_lines/blank_lines.rs | 222 ++++++++---------- 2 files changed, 98 insertions(+), 126 deletions(-) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 39c120ac92086..960c5287b16b5 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -44,7 +44,7 @@ pub(crate) fn check_logical_lines( let mut prev_indent_level = None; let indent_char = stylist.indentation().as_char(); - blank_lines_checker.check_content(tokens, 4, locator, stylist, &mut context); + blank_lines_checker.check_content(tokens, locator, stylist, &mut context); for line in &LogicalLines::from_tokens(tokens, locator) { if line.flags().contains(TokenFlags::OPERATOR) { diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index a80be49cbdd1b..a743776f5fd9a 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -10,7 +10,6 @@ use ruff_python_codegen::Stylist; use ruff_python_parser::lexer::LexResult; use ruff_python_parser::lexer::LexicalError; use ruff_python_parser::Tok; -use ruff_python_parser::TokenKind; use ruff_source_file::Locator; use ruff_text_size::TextRange; @@ -19,8 +18,6 @@ use ruff_text_size::TextSize; use crate::checkers::logical_lines::expand_indent; use crate::checkers::logical_lines::LogicalLinesContext; -use super::LogicalLine; - /// Contains variables used for the linting of blank lines. #[derive(Debug, Default)] #[allow(clippy::struct_excessive_bools)] @@ -33,7 +30,7 @@ pub(crate) struct BlankLinesChecker { /// Used for the fix in case a comment separates two non-comment logical lines to make the comment "stick" /// to the second line instead of the first. last_non_comment_line_end: TextSize, - previous_unindented_token: Option, + previous_unindented_token: Option, prev_indent_level: Option, } @@ -327,40 +324,28 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { } } -/// Returns `true` if line is a docstring only line. -fn is_docstring(line: &LogicalLine) -> bool { - !line.tokens_trimmed().is_empty() - && line - .tokens_trimmed() - .iter() - .all(|token| matches!(token.kind(), TokenKind::String)) -} - /// Returns `true` if the token is Async, Class or Def -fn is_top_level_token(token: Option) -> bool { - matches!( - token, - Some(TokenKind::Class | TokenKind::Def | TokenKind::Async) - ) +fn is_top_level_token(token: &Option) -> bool { + matches!(&token, Some(Tok::Class | Tok::Def | Tok::Async)) } /// Returns `true` if the token is At, Async, Class or Def -fn is_top_level_token_or_decorator(token: TokenKind) -> bool { - matches!( - token, - TokenKind::Class | TokenKind::Def | TokenKind::Async | TokenKind::At - ) +fn is_top_level_token_or_decorator(token: &Tok) -> bool { + matches!(&token, Tok::Class | Tok::Def | Tok::Async | Tok::At) } #[derive(Debug)] struct LogicalLineInfo { first_token: Tok, - line_is_comment_only: bool, + first_token_range: TextRange, + last_token: Tok, + last_token_range: TextRange, + is_comment_only: bool, + is_docstring: bool, indent_level: usize, blank_lines: u32, preceding_blank_lines: u32, preceding_blank_characters: TextSize, - tokens_start: TextSize, } struct LinePreprocessor<'a> { @@ -391,8 +376,11 @@ impl<'a> Iterator for LinePreprocessor<'a> { fn next(&mut self) -> Option { let mut line_is_comment_only = true; + let mut is_docstring = true; let mut first_token: Option = None; let mut first_token_range = TextRange::new(0.into(), 0.into()); + let mut last_token: Option = None; + let mut last_token_range = TextRange::new(0.into(), 0.into()); let mut parens = 0u32; let logical_line: LogicalLineInfo; @@ -407,6 +395,16 @@ impl<'a> Iterator for LinePreprocessor<'a> { line_is_comment_only = false; } + // Allow a comment to follow a docstring. + if !matches!(token, Tok::String { .. } | Tok::Comment(_)) { + is_docstring = false; + } + + if !matches!(token, Tok::Newline | Tok::NonLogicalNewline) { + last_token = Some(token.clone()); + last_token_range = range.clone(); + } + match token { Tok::Lbrace | Tok::Lpar | Tok::Lsqb => { parens = parens.saturating_add(1); @@ -415,6 +413,10 @@ impl<'a> Iterator for LinePreprocessor<'a> { parens = parens.saturating_sub(1); } Tok::Newline | Tok::NonLogicalNewline if parens == 0 => { + if !matches!(first_token, Some(Tok::String { .. })) { + is_docstring = false; + } + let range = if matches!(first_token, Some(Tok::Indent)) { first_token_range } else { @@ -437,12 +439,15 @@ impl<'a> Iterator for LinePreprocessor<'a> { logical_line = LogicalLineInfo { first_token: first_token.clone().unwrap(), - line_is_comment_only, // TODO: Have an enum flag (to unify with docstring) ? + first_token_range, + last_token: last_token.unwrap(), + last_token_range, + is_comment_only: line_is_comment_only, + is_docstring, indent_level, blank_lines: self.current_blank_lines, preceding_blank_lines: self.previous_blank_lines, preceding_blank_characters: self.current_blank_characters, - tokens_start: first_token_range.start(), }; if line_is_comment_only { @@ -467,47 +472,34 @@ impl BlankLinesChecker { pub(crate) fn check_content( &mut self, tokens: &[LexResult], - // line: &LogicalLine, - // - // indent_level: usize, - indent_size: usize, locator: &Locator, stylist: &Stylist, context: &mut LogicalLinesContext, ) { let mut prev_indent_level: Option = None; - let indent_size: usize = 4; let line_preprocessor = LinePreprocessor::new(tokens, locator); for logical_line in line_preprocessor { - dbg!(logical_line); - self.check_line(logical_line, prev_indent_level, locator, stylist, context); - prev_indent_level = Some(logical_line.indent_level); + self.check_line(&logical_line, prev_indent_level, locator, stylist, context); + prev_indent_level = Some(logical_line.indent_level.clone()); } - // TODO: have an is_docstring flag. - // TODO: Need to keep last token for: - // && !(matches!(self.follows, Follows::Def) - // && line - // .tokens_trimmed() - // .last() - // .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) - // ) - return; } pub(crate) fn check_line( &mut self, - line: LogicalLineInfo, + line: &LogicalLineInfo, prev_indent_level: Option, locator: &Locator, stylist: &Stylist, context: &mut LogicalLinesContext, ) { + let indent_size: usize = 4; + if let Status::Inside(nesting_indent) = self.class_status { - if indent_level < nesting_indent { - if line_is_comment_only { + if line.indent_level < nesting_indent { + if line.is_comment_only { self.class_status = Status::CommentAfter(nesting_indent); } else { self.class_status = Status::Outside; @@ -516,8 +508,8 @@ impl BlankLinesChecker { } if let Status::Inside(nesting_indent) = self.fn_status { - if indent_level < nesting_indent { - if line_is_comment_only { + if line.indent_level < nesting_indent { + if line.is_comment_only { self.fn_status = Status::CommentAfter(nesting_indent); } else { self.fn_status = Status::Outside; @@ -527,16 +519,16 @@ impl BlankLinesChecker { // A comment can be de-indented while still being in a class/function, in that case // we need to revert the variables. - if !line_is_comment_only { + if !line.is_comment_only { if let Status::CommentAfter(indent) = self.fn_status { - if indent_level >= indent { + if line.indent_level >= indent { self.fn_status = Status::Inside(indent); } self.fn_status = Status::Outside; } if let Status::CommentAfter(indent) = self.class_status { - if indent_level >= indent { + if line.indent_level >= indent { self.class_status = Status::Inside(indent); } self.class_status = Status::Outside; @@ -545,21 +537,21 @@ impl BlankLinesChecker { // Don't expect blank lines before the first non comment line. if self.is_not_first_logical_line { - if line.line.preceding_blank_lines == 0 + if line.preceding_blank_lines == 0 // Only applies to methods. - && first_token.kind() == TokenKind::Def + && line.first_token == Tok::Def && matches!(self.class_status, Status::Inside(_)) // The class/parent method's docstring can directly precede the def. && !matches!(self.follows, Follows::Docstring) // Do not trigger when the def follows an if/while/etc... - && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) + && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= line.indent_level) // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !matches!(self.follows, Follows::Decorator) { // E301 let mut diagnostic = Diagnostic::new( - BlankLineBetweenMethods(line.line.preceding_blank_lines), - first_token.range, + BlankLineBetweenMethods(line.preceding_blank_lines), + line.first_token_range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().as_str().to_string(), @@ -569,81 +561,76 @@ impl BlankLinesChecker { context.push_diagnostic(diagnostic); } - if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + if line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !matches!(self.follows, Follows::Decorator) // Allow groups of one-liners. - && !(matches!(self.follows, Follows::Def) - && line - .tokens_trimmed() - .last() - .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) - ) + && !(matches!(self.follows, Follows::Def) && !matches!(line.last_token, Tok::Colon)) // Only trigger on non-indented classes and functions (for example functions within an if are ignored) - && indent_level == 0 + && line.indent_level == 0 // Only apply to functions or classes. - && is_top_level_token_or_decorator(first_token.kind) + && is_top_level_token_or_decorator(&line.first_token) { // E302 let mut diagnostic = Diagnostic::new( - BlankLinesTopLevel(line.line.preceding_blank_lines), - first_token.range, + BlankLinesTopLevel(line.preceding_blank_lines), + line.first_token_range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist .line_ending() .as_str() .to_string() - .repeat((BLANK_LINES_TOP_LEVEL - line.line.preceding_blank_lines) as usize), + .repeat((BLANK_LINES_TOP_LEVEL - line.preceding_blank_lines) as usize), locator.line_start(self.last_non_comment_line_end), ))); context.push_diagnostic(diagnostic); } - if line.line.blank_lines > BLANK_LINES_TOP_LEVEL - || (indent_level > 0 && line.line.blank_lines > BLANK_LINES_METHOD_LEVEL) + if line.blank_lines > BLANK_LINES_TOP_LEVEL + || (line.indent_level > 0 && line.blank_lines > BLANK_LINES_METHOD_LEVEL) { // E303 let mut diagnostic = - Diagnostic::new(TooManyBlankLines(line.line.blank_lines), first_token.range); + Diagnostic::new(TooManyBlankLines(line.blank_lines), line.first_token_range); - let chars_to_remove = if indent_level > 0 { - line.line.preceding_blank_characters - BLANK_LINES_METHOD_LEVEL + let chars_to_remove = if line.indent_level > 0 { + line.preceding_blank_characters.to_u32() - BLANK_LINES_METHOD_LEVEL } else { - line.line.preceding_blank_characters - BLANK_LINES_TOP_LEVEL + line.preceding_blank_characters.to_u32() - BLANK_LINES_TOP_LEVEL }; - let end = locator.line_start(first_token.range.start()); + let end = locator.line_start(line.first_token_range.start()); let start = end - TextSize::new(chars_to_remove); diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); context.push_diagnostic(diagnostic); } - if matches!(self.follows, Follows::Decorator) && line.line.preceding_blank_lines > 0 { + if matches!(self.follows, Follows::Decorator) && line.preceding_blank_lines > 0 { // E304 - let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, first_token.range); + let mut diagnostic = + Diagnostic::new(BlankLineAfterDecorator, line.first_token_range); - let range = first_token.range; + let range = line.first_token_range; diagnostic.set_fix(Fix::safe_edit(Edit::deletion( - locator.line_start(range.start()) - - TextSize::new(line.line.preceding_blank_characters), + locator.line_start(range.start()) - line.preceding_blank_characters, locator.line_start(range.start()), ))); context.push_diagnostic(diagnostic); } - if line.line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL - && is_top_level_token(self.previous_unindented_token) - && indent_level == 0 - && !line_is_comment_only - && !is_top_level_token_or_decorator(first_token.kind) + if line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL + && is_top_level_token(&self.previous_unindented_token) + && line.indent_level == 0 + && !line.is_comment_only + && !is_top_level_token_or_decorator(&line.first_token) { // E305 let mut diagnostic = Diagnostic::new( - BlankLinesAfterFunctionOrClass(line.line.blank_lines), - first_token.range, + BlankLinesAfterFunctionOrClass(line.blank_lines), + line.first_token_range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( @@ -651,91 +638,76 @@ impl BlankLinesChecker { .line_ending() .as_str() .to_string() - .repeat((BLANK_LINES_TOP_LEVEL - line.line.blank_lines) as usize), - locator.line_start(first_token.range.start()), + .repeat((BLANK_LINES_TOP_LEVEL - line.blank_lines) as usize), + locator.line_start(line.first_token_range.start()), ))); context.push_diagnostic(diagnostic); } - if line.line.preceding_blank_lines == 0 + if line.preceding_blank_lines == 0 // Only apply to nested functions. && matches!(self.fn_status, Status::Inside(_)) - && is_top_level_token_or_decorator(first_token.kind) + && is_top_level_token_or_decorator(&line.first_token) // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !matches!(self.follows, Follows::Decorator) // The class's docstring can directly precede the first function. && !matches!(self.follows, Follows::Docstring) // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). - && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= indent_level) + && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= line.indent_level) // Allow groups of one-liners. - && !(matches!(self.follows, Follows::Def) - && line - .tokens_trimmed() - .last() - .map_or(false, |token| !matches!(token.kind(), TokenKind::Colon)) - ) + && !(matches!(self.follows, Follows::Def) && !matches!(line.last_token, Tok::Colon)) { // E306 let mut diagnostic = Diagnostic::new( - BlankLinesBeforeNestedDefinition(line.line.blank_lines), - first_token.range, + BlankLinesBeforeNestedDefinition(line.blank_lines), + line.first_token_range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().as_str().to_string(), - locator.line_start(first_token.range.start()), + locator.line_start(line.first_token_range.start()), ))); context.push_diagnostic(diagnostic); } } - match first_token.kind() { - TokenKind::Class => { + match line.first_token { + Tok::Class => { if matches!(self.class_status, Status::Outside) { - self.class_status = Status::Inside(indent_level + indent_size); + self.class_status = Status::Inside(line.indent_level + indent_size); } self.follows = Follows::Other; } - TokenKind::At => { + Tok::At => { self.follows = Follows::Decorator; } - TokenKind::Def | TokenKind::Async => { + Tok::Def | Tok::Async => { if matches!(self.fn_status, Status::Outside) { - self.fn_status = Status::Inside(indent_level + indent_size); + self.fn_status = Status::Inside(line.indent_level + indent_size); } self.follows = Follows::Def; } - TokenKind::Comment => {} + Tok::Comment(_) => {} _ => { self.follows = Follows::Other; } } - if !line_is_comment_only { + if !line.is_comment_only { if !self.is_not_first_logical_line { self.is_not_first_logical_line = true; } - if is_docstring(line) { + if line.is_docstring { self.follows = Follows::Docstring; } - self.last_non_comment_line_end = line - .tokens() - .last() - .expect("Line to contain at least one token.") - .range - .end(); - - if indent_level == 0 && !line.tokens_trimmed().is_empty() { - self.previous_unindented_token = Some( - line.tokens_trimmed() - .first() - .expect("Previously checked.") - .kind, - ); + self.last_non_comment_line_end = line.last_token_range.end(); + + if line.indent_level == 0 && !matches!(line.first_token, Tok::Comment(_)) { + self.previous_unindented_token = Some(line.first_token.clone()); } } } From 92da66c5a784037c9c40df94dd2678d6dd677ff0 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 16 Dec 2023 15:42:20 +0900 Subject: [PATCH 052/122] Remove blank line counting in logical lines. --- .../rules/logical_lines/blank_lines.rs | 3 +-- .../pycodestyle/rules/logical_lines/mod.rs | 25 +------------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index a743776f5fd9a..84489b48fd93b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -31,7 +31,6 @@ pub(crate) struct BlankLinesChecker { /// to the second line instead of the first. last_non_comment_line_end: TextSize, previous_unindented_token: Option, - prev_indent_level: Option, } /// Number of blank lines around top level classes and functions. @@ -487,7 +486,7 @@ impl BlankLinesChecker { return; } - pub(crate) fn check_line( + fn check_line( &mut self, line: &LogicalLineInfo, prev_indent_level: Option, diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs index 4a8bb4befd821..dfaf5a23fba48 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs @@ -414,12 +414,6 @@ struct LogicalLinesBuilder { tokens: Vec, lines: Vec, current_line: CurrentLine, - /// Number of previous consecutive blank lines. - previous_blank_lines: u32, - /// Number of consecutive blank lines. - current_blank_lines: u32, - /// Number of blank characters in the blank lines (\n vs \r\n for example). - current_blank_characters: u32, } impl LogicalLinesBuilder { @@ -481,26 +475,12 @@ impl LogicalLinesBuilder { let is_empty = self.tokens[self.current_line.tokens_start as usize..end as usize] .iter() .all(|token| token.kind.is_newline()); - if is_empty { - self.current_blank_lines += 1; - self.current_blank_characters += end - self.current_line.tokens_start; - } else { - if self.previous_blank_lines < self.current_blank_lines { - self.previous_blank_lines = self.current_blank_lines; - } + if !is_empty { self.lines.push(Line { flags: self.current_line.flags, - blank_lines: self.current_blank_lines, - preceding_blank_lines: self.previous_blank_lines, - preceding_blank_characters: self.current_blank_characters, tokens_start: self.current_line.tokens_start, tokens_end: end, }); - if self.current_line.flags != TokenFlags::COMMENT { - self.previous_blank_lines = 0; - } - self.current_blank_lines = 0; - self.current_blank_characters = 0; } self.current_line = CurrentLine { @@ -524,9 +504,6 @@ impl LogicalLinesBuilder { #[derive(Debug, Clone)] struct Line { flags: TokenFlags, - blank_lines: u32, - preceding_blank_lines: u32, - preceding_blank_characters: u32, tokens_start: u32, tokens_end: u32, } From 7099fca02efcbc0b06dca621924a2f8e2a78c8c8 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 16 Dec 2023 16:25:39 +0900 Subject: [PATCH 053/122] Bug fixes. --- .../rules/logical_lines/blank_lines.rs | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 84489b48fd93b..9a2590d7f5c74 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -390,18 +390,31 @@ impl<'a> Iterator for LinePreprocessor<'a> { first_token_range = range.clone(); } - if !matches!(token, Tok::Comment(_)) { + if !matches!( + token, + Tok::Comment(_) | Tok::Indent | Tok::Dedent | Tok::Newline | Tok::NonLogicalNewline + ) { line_is_comment_only = false; } // Allow a comment to follow a docstring. - if !matches!(token, Tok::String { .. } | Tok::Comment(_)) { + if !matches!( + token, + Tok::String { .. } + | Tok::Comment(_) + | Tok::Indent + | Tok::Dedent + | Tok::Newline + | Tok::NonLogicalNewline + ) { is_docstring = false; } - if !matches!(token, Tok::Newline | Tok::NonLogicalNewline) { + if !matches!( + token, + Tok::Indent | Tok::Dedent | Tok::Newline | Tok::NonLogicalNewline + ) { last_token = Some(token.clone()); - last_token_range = range.clone(); } match token { @@ -412,6 +425,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { parens = parens.saturating_sub(1); } Tok::Newline | Tok::NonLogicalNewline if parens == 0 => { + last_token_range = range.clone(); + if !matches!(first_token, Some(Tok::String { .. })) { is_docstring = false; } @@ -429,7 +444,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { // Empty line if matches!(first_token, Some(Tok::NonLogicalNewline)) { self.current_blank_lines += 1; - self.current_blank_characters += range.end() - first_token_range.start(); + self.current_blank_characters += + range.end() - first_token_range.start() + TextSize::new(1); return self.next(); } else { if self.previous_blank_lines < self.current_blank_lines { @@ -449,7 +465,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { preceding_blank_characters: self.current_blank_characters, }; - if line_is_comment_only { + if !line_is_comment_only { self.previous_blank_lines = 0; } self.current_blank_lines = 0; From 5a59006fd0bf6fd9f326550b30d8d1389283c83a Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 16 Dec 2023 16:35:56 +0900 Subject: [PATCH 054/122] Clippy fixes. --- .../rules/logical_lines/blank_lines.rs | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index 9a2590d7f5c74..a286af21af71a 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -361,7 +361,7 @@ struct LinePreprocessor<'a> { impl<'a> LinePreprocessor<'a> { fn new(tokens: &'a [LexResult], locator: &'a Locator) -> LinePreprocessor<'a> { LinePreprocessor { - tokens: tokens.into_iter().flatten(), + tokens: tokens.iter().flatten(), locator, previous_blank_lines: 0, current_blank_lines: 0, @@ -379,7 +379,6 @@ impl<'a> Iterator for LinePreprocessor<'a> { let mut first_token: Option = None; let mut first_token_range = TextRange::new(0.into(), 0.into()); let mut last_token: Option = None; - let mut last_token_range = TextRange::new(0.into(), 0.into()); let mut parens = 0u32; let logical_line: LogicalLineInfo; @@ -387,7 +386,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { while let Some((token, range)) = self.tokens.next() { if first_token.is_none() && !matches!(token, Tok::Indent | Tok::Dedent | Tok::Newline) { first_token = Some(token.clone()); - first_token_range = range.clone(); + first_token_range = *range; } if !matches!( @@ -425,7 +424,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { parens = parens.saturating_sub(1); } Tok::Newline | Tok::NonLogicalNewline if parens == 0 => { - last_token_range = range.clone(); + let last_token_range = *range; if !matches!(first_token, Some(Tok::String { .. })) { is_docstring = false; @@ -447,37 +446,37 @@ impl<'a> Iterator for LinePreprocessor<'a> { self.current_blank_characters += range.end() - first_token_range.start() + TextSize::new(1); return self.next(); - } else { - if self.previous_blank_lines < self.current_blank_lines { - self.previous_blank_lines = self.current_blank_lines; - } - - logical_line = LogicalLineInfo { - first_token: first_token.clone().unwrap(), - first_token_range, - last_token: last_token.unwrap(), - last_token_range, - is_comment_only: line_is_comment_only, - is_docstring, - indent_level, - blank_lines: self.current_blank_lines, - preceding_blank_lines: self.previous_blank_lines, - preceding_blank_characters: self.current_blank_characters, - }; - - if !line_is_comment_only { - self.previous_blank_lines = 0; - } - self.current_blank_lines = 0; - self.current_blank_characters = 0.into(); - return Some(logical_line); } + + if self.previous_blank_lines < self.current_blank_lines { + self.previous_blank_lines = self.current_blank_lines; + } + + logical_line = LogicalLineInfo { + first_token: first_token.clone().unwrap(), + first_token_range, + last_token: last_token.unwrap(), + last_token_range, + is_comment_only: line_is_comment_only, + is_docstring, + indent_level, + blank_lines: self.current_blank_lines, + preceding_blank_lines: self.previous_blank_lines, + preceding_blank_characters: self.current_blank_characters, + }; + + if !line_is_comment_only { + self.previous_blank_lines = 0; + } + self.current_blank_lines = 0; + self.current_blank_characters = 0.into(); + return Some(logical_line); } _ => {} } } - return None; + None } } @@ -496,10 +495,8 @@ impl BlankLinesChecker { for logical_line in line_preprocessor { self.check_line(&logical_line, prev_indent_level, locator, stylist, context); - prev_indent_level = Some(logical_line.indent_level.clone()); + prev_indent_level = Some(logical_line.indent_level); } - - return; } fn check_line( From f1205a187d000494bfba453d246bb47586449b73 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 16 Dec 2023 17:07:39 +0900 Subject: [PATCH 055/122] Add comment. --- .../rules/pycodestyle/rules/logical_lines/blank_lines.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs index a286af21af71a..d267a3270537c 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs @@ -347,6 +347,8 @@ struct LogicalLineInfo { preceding_blank_characters: TextSize, } +/// Iterator that processes tokens until a full logical line (or comment line) is "built". +/// It then returns characteristics of that logical line (see `LogicalLineInfo`). struct LinePreprocessor<'a> { tokens: Flatten>>, locator: &'a Locator<'a>, @@ -381,8 +383,6 @@ impl<'a> Iterator for LinePreprocessor<'a> { let mut last_token: Option = None; let mut parens = 0u32; - let logical_line: LogicalLineInfo; - while let Some((token, range)) = self.tokens.next() { if first_token.is_none() && !matches!(token, Tok::Indent | Tok::Dedent | Tok::Newline) { first_token = Some(token.clone()); @@ -452,7 +452,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { self.previous_blank_lines = self.current_blank_lines; } - logical_line = LogicalLineInfo { + let logical_line = LogicalLineInfo { first_token: first_token.clone().unwrap(), first_token_range, last_token: last_token.unwrap(), From 692793763dc7d506f9ea00cd0218344ace84fcc1 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 24 Dec 2023 22:24:55 +0900 Subject: [PATCH 056/122] Move the blank_lines outside the logical_lines module. --- .../ruff_linter/src/checkers/logical_lines.rs | 18 ++++++++++++++---- crates/ruff_linter/src/codes.rs | 12 ++++++------ .../rules/{logical_lines => }/blank_lines.rs | 0 .../pycodestyle/rules/logical_lines/mod.rs | 2 -- .../src/rules/pycodestyle/rules/mod.rs | 2 ++ 5 files changed, 22 insertions(+), 12 deletions(-) rename crates/ruff_linter/src/rules/pycodestyle/rules/{logical_lines => }/blank_lines.rs (100%) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 960c5287b16b5..cefe436dc697f 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -1,3 +1,4 @@ +use crate::registry::Rule; use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_python_codegen::Stylist; use ruff_python_parser::lexer::LexResult; @@ -10,9 +11,9 @@ use crate::rules::pycodestyle::rules::logical_lines::{ extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword, missing_whitespace_around_operator, space_after_comma, space_around_operator, whitespace_around_keywords, whitespace_around_named_parameter_equals, - whitespace_before_comment, whitespace_before_parameters, BlankLinesChecker, LogicalLines, - TokenFlags, + whitespace_before_comment, whitespace_before_parameters, LogicalLines, TokenFlags, }; +use crate::rules::pycodestyle::rules::BlankLinesChecker; use crate::settings::LinterSettings; /// Return the amount of indentation, expanding tabs to the next multiple of 8. @@ -39,12 +40,21 @@ pub(crate) fn check_logical_lines( ) -> Vec { let mut context = LogicalLinesContext::new(settings); - let mut blank_lines_checker = BlankLinesChecker::default(); let mut non_comment_prev_line = None; let mut prev_indent_level = None; let indent_char = stylist.indentation().as_char(); - blank_lines_checker.check_content(tokens, locator, stylist, &mut context); + if settings.rules.any_enabled(&[ + Rule::BlankLineBetweenMethods, + Rule::BlankLinesTopLevel, + Rule::TooManyBlankLines, + Rule::BlankLineAfterDecorator, + Rule::BlankLinesAfterFunctionOrClass, + Rule::BlankLinesBeforeNestedDefinition, + ]) { + let mut blank_lines_checker = BlankLinesChecker::default(); + blank_lines_checker.check_content(tokens, locator, stylist, &mut context); + } for line in &LogicalLines::from_tokens(tokens, locator) { if line.flags().contains(TokenFlags::OPERATOR) { diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index b8fa9c151035a..b5f68cf088c92 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -133,17 +133,17 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { #[allow(deprecated)] (Pycodestyle, "E275") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAfterKeyword), #[allow(deprecated)] - (Pycodestyle, "E301") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLineBetweenMethods), + (Pycodestyle, "E301") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLineBetweenMethods), #[allow(deprecated)] - (Pycodestyle, "E302") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLinesTopLevel), + (Pycodestyle, "E302") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLinesTopLevel), #[allow(deprecated)] - (Pycodestyle, "E303") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TooManyBlankLines), + (Pycodestyle, "E303") => (RuleGroup::Nursery, rules::pycodestyle::rules::TooManyBlankLines), #[allow(deprecated)] - (Pycodestyle, "E304") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLineAfterDecorator), + (Pycodestyle, "E304") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLineAfterDecorator), #[allow(deprecated)] - (Pycodestyle, "E305") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLinesAfterFunctionOrClass), + (Pycodestyle, "E305") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLinesAfterFunctionOrClass), #[allow(deprecated)] - (Pycodestyle, "E306") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::BlankLinesBeforeNestedDefinition), + (Pycodestyle, "E306") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLinesBeforeNestedDefinition), (Pycodestyle, "E401") => (RuleGroup::Stable, rules::pycodestyle::rules::MultipleImportsOnOneLine), (Pycodestyle, "E402") => (RuleGroup::Stable, rules::pycodestyle::rules::ModuleImportNotAtTopOfFile), (Pycodestyle, "E501") => (RuleGroup::Stable, rules::pycodestyle::rules::LineTooLong), diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs similarity index 100% rename from crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/blank_lines.rs rename to crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs index dfaf5a23fba48..8fcfca6d769ef 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/mod.rs @@ -1,4 +1,3 @@ -pub(crate) use blank_lines::*; pub(crate) use extraneous_whitespace::*; pub(crate) use indentation::*; pub(crate) use missing_whitespace::*; @@ -21,7 +20,6 @@ use ruff_python_parser::TokenKind; use ruff_python_trivia::is_python_whitespace; use ruff_source_file::Locator; -mod blank_lines; mod extraneous_whitespace; mod indentation; mod missing_whitespace; diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/mod.rs index 33b8cc49609bc..f033ab3a959e8 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/mod.rs @@ -2,6 +2,7 @@ pub(crate) use ambiguous_class_name::*; pub(crate) use ambiguous_function_name::*; pub(crate) use ambiguous_variable_name::*; pub(crate) use bare_except::*; +pub(crate) use blank_lines::*; pub(crate) use compound_statements::*; pub(crate) use doc_line_too_long::*; pub(crate) use errors::*; @@ -23,6 +24,7 @@ mod ambiguous_class_name; mod ambiguous_function_name; mod ambiguous_variable_name; mod bare_except; +mod blank_lines; mod compound_statements; mod doc_line_too_long; mod errors; From 94fe6efed6537705e2859d279197d557a7254fd8 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 24 Dec 2023 22:48:50 +0900 Subject: [PATCH 057/122] Move check to the tokens' file. --- .../ruff_linter/src/checkers/logical_lines.rs | 14 ----------- crates/ruff_linter/src/checkers/tokens.rs | 14 +++++++++++ crates/ruff_linter/src/linter.rs | 1 + .../rules/pycodestyle/rules/blank_lines.rs | 24 ++++++++++++------- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 53c8b5d39a029..6cf8f4c4f80da 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -1,4 +1,3 @@ -use crate::registry::Rule; use ruff_diagnostics::Diagnostic; use ruff_python_codegen::Stylist; use ruff_python_parser::lexer::LexResult; @@ -13,7 +12,6 @@ use crate::rules::pycodestyle::rules::logical_lines::{ whitespace_around_keywords, whitespace_around_named_parameter_equals, whitespace_before_comment, whitespace_before_parameters, LogicalLines, TokenFlags, }; -use crate::rules::pycodestyle::rules::BlankLinesChecker; use crate::settings::LinterSettings; /// Return the amount of indentation, expanding tabs to the next multiple of 8. @@ -44,18 +42,6 @@ pub(crate) fn check_logical_lines( let mut prev_indent_level = None; let indent_char = stylist.indentation().as_char(); - if settings.rules.any_enabled(&[ - Rule::BlankLineBetweenMethods, - Rule::BlankLinesTopLevel, - Rule::TooManyBlankLines, - Rule::BlankLineAfterDecorator, - Rule::BlankLinesAfterFunctionOrClass, - Rule::BlankLinesBeforeNestedDefinition, - ]) { - let mut blank_lines_checker = BlankLinesChecker::default(); - blank_lines_checker.check_content(tokens, locator, stylist, &mut context); - } - for line in &LogicalLines::from_tokens(tokens, locator) { if line.flags().contains(TokenFlags::OPERATOR) { space_around_operator(&line, &mut context); diff --git a/crates/ruff_linter/src/checkers/tokens.rs b/crates/ruff_linter/src/checkers/tokens.rs index ae7643d92f7e1..d61c885850bd0 100644 --- a/crates/ruff_linter/src/checkers/tokens.rs +++ b/crates/ruff_linter/src/checkers/tokens.rs @@ -4,6 +4,7 @@ use std::path::Path; use ruff_notebook::CellOffsets; use ruff_python_ast::PySourceType; +use ruff_python_codegen::Stylist; use ruff_python_parser::lexer::LexResult; use ruff_python_parser::Tok; @@ -14,6 +15,7 @@ use ruff_source_file::Locator; use crate::directives::TodoComment; use crate::lex::docstring_detection::StateMachine; use crate::registry::{AsRule, Rule}; +use crate::rules::pycodestyle::rules::BlankLinesChecker; use crate::rules::ruff::rules::Context; use crate::rules::{ eradicate, flake8_commas, flake8_executable, flake8_fixme, flake8_implicit_str_concat, @@ -26,12 +28,24 @@ pub(crate) fn check_tokens( path: &Path, locator: &Locator, indexer: &Indexer, + stylist: &Stylist, settings: &LinterSettings, source_type: PySourceType, cell_offsets: Option<&CellOffsets>, ) -> Vec { let mut diagnostics: Vec = vec![]; + if settings.rules.any_enabled(&[ + Rule::BlankLineBetweenMethods, + Rule::BlankLinesTopLevel, + Rule::TooManyBlankLines, + Rule::BlankLineAfterDecorator, + Rule::BlankLinesAfterFunctionOrClass, + Rule::BlankLinesBeforeNestedDefinition, + ]) { + let mut blank_lines_checker = BlankLinesChecker::default(); + blank_lines_checker.check_content(tokens, locator, stylist, &mut diagnostics); + } if settings.rules.enabled(Rule::BlanketNOQA) { pygrep_hooks::rules::blanket_noqa(&mut diagnostics, indexer, locator); } diff --git a/crates/ruff_linter/src/linter.rs b/crates/ruff_linter/src/linter.rs index 2d81cc961c644..53f98baeee5fe 100644 --- a/crates/ruff_linter/src/linter.rs +++ b/crates/ruff_linter/src/linter.rs @@ -107,6 +107,7 @@ pub fn check_path( path, locator, indexer, + stylist, settings, source_type, source_kind.as_ipy_notebook().map(Notebook::cell_offsets), diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index d267a3270537c..e42ca1877c2cd 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -488,13 +488,19 @@ impl BlankLinesChecker { tokens: &[LexResult], locator: &Locator, stylist: &Stylist, - context: &mut LogicalLinesContext, + diagnostics: &mut Vec, ) { let mut prev_indent_level: Option = None; let line_preprocessor = LinePreprocessor::new(tokens, locator); for logical_line in line_preprocessor { - self.check_line(&logical_line, prev_indent_level, locator, stylist, context); + self.check_line( + &logical_line, + prev_indent_level, + locator, + stylist, + diagnostics, + ); prev_indent_level = Some(logical_line.indent_level); } } @@ -505,7 +511,7 @@ impl BlankLinesChecker { prev_indent_level: Option, locator: &Locator, stylist: &Stylist, - context: &mut LogicalLinesContext, + diagnostics: &mut Vec, ) { let indent_size: usize = 4; @@ -570,7 +576,7 @@ impl BlankLinesChecker { locator.line_start(self.last_non_comment_line_end), ))); - context.push_diagnostic(diagnostic); + diagnostics.push(diagnostic); } if line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL @@ -597,7 +603,7 @@ impl BlankLinesChecker { locator.line_start(self.last_non_comment_line_end), ))); - context.push_diagnostic(diagnostic); + diagnostics.push(diagnostic); } if line.blank_lines > BLANK_LINES_TOP_LEVEL @@ -616,7 +622,7 @@ impl BlankLinesChecker { let start = end - TextSize::new(chars_to_remove); diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); - context.push_diagnostic(diagnostic); + diagnostics.push(diagnostic); } if matches!(self.follows, Follows::Decorator) && line.preceding_blank_lines > 0 { @@ -630,7 +636,7 @@ impl BlankLinesChecker { locator.line_start(range.start()), ))); - context.push_diagnostic(diagnostic); + diagnostics.push(diagnostic); } if line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL @@ -654,7 +660,7 @@ impl BlankLinesChecker { locator.line_start(line.first_token_range.start()), ))); - context.push_diagnostic(diagnostic); + diagnostics.push(diagnostic); } if line.preceding_blank_lines == 0 @@ -681,7 +687,7 @@ impl BlankLinesChecker { locator.line_start(line.first_token_range.start()), ))); - context.push_diagnostic(diagnostic); + diagnostics.push(diagnostic); } } From f2d49574fb091e5eb3327399b53d0a0e8d73d0f7 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 24 Dec 2023 22:55:35 +0900 Subject: [PATCH 058/122] Remove unused import. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index e42ca1877c2cd..8ad7b855cd6ed 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -16,7 +16,6 @@ use ruff_text_size::TextRange; use ruff_text_size::TextSize; use crate::checkers::logical_lines::expand_indent; -use crate::checkers::logical_lines::LogicalLinesContext; /// Contains variables used for the linting of blank lines. #[derive(Debug, Default)] From c9cf6b6519a934392ae7c7f6968881f07fbb09ca Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 24 Dec 2023 23:07:45 +0900 Subject: [PATCH 059/122] Fix rule placement in the registry. --- crates/ruff_linter/src/checkers/tokens.rs | 1 + crates/ruff_linter/src/registry.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/ruff_linter/src/checkers/tokens.rs b/crates/ruff_linter/src/checkers/tokens.rs index d61c885850bd0..aa25cfe8cdf6e 100644 --- a/crates/ruff_linter/src/checkers/tokens.rs +++ b/crates/ruff_linter/src/checkers/tokens.rs @@ -46,6 +46,7 @@ pub(crate) fn check_tokens( let mut blank_lines_checker = BlankLinesChecker::default(); blank_lines_checker.check_content(tokens, locator, stylist, &mut diagnostics); } + if settings.rules.enabled(Rule::BlanketNOQA) { pygrep_hooks::rules::blanket_noqa(&mut diagnostics, indexer, locator); } diff --git a/crates/ruff_linter/src/registry.rs b/crates/ruff_linter/src/registry.rs index a9a2c400c27e7..d6ba0b44b4d8a 100644 --- a/crates/ruff_linter/src/registry.rs +++ b/crates/ruff_linter/src/registry.rs @@ -264,6 +264,11 @@ impl Rule { | Rule::BadQuotesMultilineString | Rule::BlanketNOQA | Rule::BlanketTypeIgnore + | Rule::BlankLineAfterDecorator + | Rule::BlankLineBetweenMethods + | Rule::BlankLinesAfterFunctionOrClass + | Rule::BlankLinesBeforeNestedDefinition + | Rule::BlankLinesTopLevel | Rule::CommentedOutCode | Rule::ExtraneousParentheses | Rule::InvalidCharacterBackspace @@ -295,6 +300,7 @@ impl Rule { | Rule::ShebangNotFirstLine | Rule::SingleLineImplicitStringConcatenation | Rule::TabIndentation + | Rule::TooManyBlankLines | Rule::TrailingCommaOnBareTuple | Rule::TypeCommentInStub | Rule::UselessSemicolon @@ -302,12 +308,7 @@ impl Rule { Rule::IOError => LintSource::Io, Rule::UnsortedImports | Rule::MissingRequiredImport => LintSource::Imports, Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => LintSource::Filesystem, - Rule::BlankLineAfterDecorator - | Rule::BlankLineBetweenMethods - | Rule::BlankLinesAfterFunctionOrClass - | Rule::BlankLinesBeforeNestedDefinition - | Rule::BlankLinesTopLevel - | Rule::IndentationWithInvalidMultiple + Rule::IndentationWithInvalidMultiple | Rule::IndentationWithInvalidMultipleComment | Rule::MissingWhitespace | Rule::MissingWhitespaceAfterKeyword @@ -333,7 +334,6 @@ impl Rule { | Rule::TabBeforeKeyword | Rule::TabBeforeOperator | Rule::TooFewSpacesBeforeInlineComment - | Rule::TooManyBlankLines | Rule::UnexpectedIndentation | Rule::UnexpectedIndentationComment | Rule::UnexpectedSpacesAroundKeywordParameterEquals From 6f3aa5cf76a1961c4a102665caad42a5f2ac9547 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 24 Dec 2023 23:19:59 +0900 Subject: [PATCH 060/122] Ignore clippy warnings. --- crates/ruff_linter/src/checkers/tokens.rs | 1 + crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/ruff_linter/src/checkers/tokens.rs b/crates/ruff_linter/src/checkers/tokens.rs index aa25cfe8cdf6e..4c902ed9ac4a7 100644 --- a/crates/ruff_linter/src/checkers/tokens.rs +++ b/crates/ruff_linter/src/checkers/tokens.rs @@ -23,6 +23,7 @@ use crate::rules::{ }; use crate::settings::LinterSettings; +#[allow(clippy::too_many_arguments)] pub(crate) fn check_tokens( tokens: &[LexResult], path: &Path, diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 8ad7b855cd6ed..eee36a3c257be 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -504,6 +504,7 @@ impl BlankLinesChecker { } } + #[allow(clippy::nonminimal_bool)] fn check_line( &mut self, line: &LogicalLineInfo, From 55a918da649603b9b439e62582645ec81b26a670 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Mon, 25 Dec 2023 08:23:03 +0900 Subject: [PATCH 061/122] Split if conditions into multiple ifs. --- .../rules/pycodestyle/rules/blank_lines.rs | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index eee36a3c257be..c59717f27af0a 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -383,35 +383,27 @@ impl<'a> Iterator for LinePreprocessor<'a> { let mut parens = 0u32; while let Some((token, range)) = self.tokens.next() { - if first_token.is_none() && !matches!(token, Tok::Indent | Tok::Dedent | Tok::Newline) { - first_token = Some(token.clone()); - first_token_range = *range; + if matches!(token, Tok::Indent | Tok::Dedent) { + continue; } - if !matches!( - token, - Tok::Comment(_) | Tok::Indent | Tok::Dedent | Tok::Newline | Tok::NonLogicalNewline - ) { - line_is_comment_only = false; - } + if !matches!(token, Tok::Newline) { + if first_token.is_none() { + first_token = Some(token.clone()); + first_token_range = *range; + } - // Allow a comment to follow a docstring. - if !matches!( - token, - Tok::String { .. } - | Tok::Comment(_) - | Tok::Indent - | Tok::Dedent - | Tok::Newline - | Tok::NonLogicalNewline - ) { - is_docstring = false; - } + if !matches!(token, Tok::NonLogicalNewline) { + if !matches!(token, Tok::Comment(_)) { + line_is_comment_only = false; + } + + // Allow a comment to follow a docstring. + if !matches!(token, Tok::String { .. } | Tok::Comment(_)) { + is_docstring = false; + } + } - if !matches!( - token, - Tok::Indent | Tok::Dedent | Tok::Newline | Tok::NonLogicalNewline - ) { last_token = Some(token.clone()); } From 9f2268c99ae18648c175768a4d1795754540c942 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 29 Dec 2023 12:37:37 +0900 Subject: [PATCH 062/122] Fix 2 false positives linked to indentation. --- .../test/fixtures/pycodestyle/E30.py | 16 + .../rules/pycodestyle/rules/blank_lines.rs | 4 +- ...ules__pycodestyle__tests__E301_E30.py.snap | 52 +-- ...ules__pycodestyle__tests__E302_E30.py.snap | 270 +++++++-------- ...ules__pycodestyle__tests__E303_E30.py.snap | 196 +++++------ ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 316 +++++++++--------- 8 files changed, 512 insertions(+), 494 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index d09f9e67a95b0..a4435efaf0d19 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -356,6 +356,22 @@ def f(): # end +# no error +class MyClass: + # comment + def method(self) -> None: + pass +# end + + +# no error +def function1(): + # Comment + def function2(): + pass +# end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index c59717f27af0a..1b9022854fdc3 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -492,7 +492,9 @@ impl BlankLinesChecker { stylist, diagnostics, ); - prev_indent_level = Some(logical_line.indent_level); + if !logical_line.is_comment_only { + prev_indent_level = Some(logical_line.indent_level); + } } } diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index 8924ed271d707..66514671df460 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:364:5: E301 [*] Expected 1 blank line, found 0 +E30.py:380:5: E301 [*] Expected 1 blank line, found 0 | -362 | def func1(): -363 | pass -364 | def func2(): +378 | def func1(): +379 | pass +380 | def func2(): | ^^^ E301 -365 | pass -366 | # end +381 | pass +382 | # end | = help: Add missing blank line(s) ℹ Safe fix -361 361 | -362 362 | def func1(): -363 363 | pass - 364 |+ -364 365 | def func2(): -365 366 | pass -366 367 | # end +377 377 | +378 378 | def func1(): +379 379 | pass + 380 |+ +380 381 | def func2(): +381 382 | pass +382 383 | # end -E30.py:375:5: E301 [*] Expected 1 blank line, found 0 +E30.py:391:5: E301 [*] Expected 1 blank line, found 0 | -373 | pass -374 | # comment -375 | def fn2(): +389 | pass +390 | # comment +391 | def fn2(): | ^^^ E301 -376 | pass -377 | # end +392 | pass +393 | # end | = help: Add missing blank line(s) ℹ Safe fix -371 371 | -372 372 | def fn1(): -373 373 | pass - 374 |+ -374 375 | # comment -375 376 | def fn2(): -376 377 | pass +387 387 | +388 388 | def fn1(): +389 389 | pass + 390 |+ +390 391 | # comment +391 392 | def fn2(): +392 393 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index d9b81fcf89e81..85175ee186825 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,186 +1,186 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:382:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:398:1: E302 [*] Expected 2 blank lines, found 0 | -380 | # E302 -381 | """Main module.""" -382 | def fn(): +396 | # E302 +397 | """Main module.""" +398 | def fn(): | ^^^ E302 -383 | pass -384 | # end +399 | pass +400 | # end | = help: Add missing blank line(s) ℹ Safe fix -379 379 | -380 380 | # E302 -381 381 | """Main module.""" - 382 |+ - 383 |+ -382 384 | def fn(): -383 385 | pass -384 386 | # end - -E30.py:389:1: E302 [*] Expected 2 blank lines, found 0 - | -387 | # E302 -388 | import sys -389 | def get_sys_path(): +395 395 | +396 396 | # E302 +397 397 | """Main module.""" + 398 |+ + 399 |+ +398 400 | def fn(): +399 401 | pass +400 402 | # end + +E30.py:405:1: E302 [*] Expected 2 blank lines, found 0 + | +403 | # E302 +404 | import sys +405 | def get_sys_path(): | ^^^ E302 -390 | return sys.path -391 | # end +406 | return sys.path +407 | # end | = help: Add missing blank line(s) ℹ Safe fix -386 386 | -387 387 | # E302 -388 388 | import sys - 389 |+ - 390 |+ -389 391 | def get_sys_path(): -390 392 | return sys.path -391 393 | # end - -E30.py:398:1: E302 [*] Expected 2 blank lines, found 1 - | -396 | pass -397 | -398 | def b(): +402 402 | +403 403 | # E302 +404 404 | import sys + 405 |+ + 406 |+ +405 407 | def get_sys_path(): +406 408 | return sys.path +407 409 | # end + +E30.py:414:1: E302 [*] Expected 2 blank lines, found 1 + | +412 | pass +413 | +414 | def b(): | ^^^ E302 -399 | pass -400 | # end +415 | pass +416 | # end | = help: Add missing blank line(s) ℹ Safe fix -395 395 | def a(): -396 396 | pass -397 397 | - 398 |+ -398 399 | def b(): -399 400 | pass -400 401 | # end - -E30.py:409:1: E302 [*] Expected 2 blank lines, found 1 - | -407 | # comment -408 | -409 | def b(): +411 411 | def a(): +412 412 | pass +413 413 | + 414 |+ +414 415 | def b(): +415 416 | pass +416 417 | # end + +E30.py:425:1: E302 [*] Expected 2 blank lines, found 1 + | +423 | # comment +424 | +425 | def b(): | ^^^ E302 -410 | pass -411 | # end +426 | pass +427 | # end | = help: Add missing blank line(s) ℹ Safe fix -404 404 | def a(): -405 405 | pass -406 406 | - 407 |+ -407 408 | # comment -408 409 | -409 410 | def b(): - -E30.py:418:1: E302 [*] Expected 2 blank lines, found 1 - | -416 | pass -417 | -418 | async def b(): +420 420 | def a(): +421 421 | pass +422 422 | + 423 |+ +423 424 | # comment +424 425 | +425 426 | def b(): + +E30.py:434:1: E302 [*] Expected 2 blank lines, found 1 + | +432 | pass +433 | +434 | async def b(): | ^^^^^ E302 -419 | pass -420 | # end +435 | pass +436 | # end | = help: Add missing blank line(s) ℹ Safe fix -415 415 | def a(): -416 416 | pass -417 417 | - 418 |+ -418 419 | async def b(): -419 420 | pass -420 421 | # end - -E30.py:427:1: E302 [*] Expected 2 blank lines, found 1 - | -425 | pass -426 | -427 | async def x(y: int = 1): +431 431 | def a(): +432 432 | pass +433 433 | + 434 |+ +434 435 | async def b(): +435 436 | pass +436 437 | # end + +E30.py:443:1: E302 [*] Expected 2 blank lines, found 1 + | +441 | pass +442 | +443 | async def x(y: int = 1): | ^^^^^ E302 -428 | pass -429 | # end +444 | pass +445 | # end | = help: Add missing blank line(s) ℹ Safe fix -424 424 | async def x(): -425 425 | pass -426 426 | - 427 |+ -427 428 | async def x(y: int = 1): -428 429 | pass -429 430 | # end - -E30.py:435:1: E302 [*] Expected 2 blank lines, found 0 - | -433 | def bar(): -434 | pass -435 | def baz(): pass +440 440 | async def x(): +441 441 | pass +442 442 | + 443 |+ +443 444 | async def x(y: int = 1): +444 445 | pass +445 446 | # end + +E30.py:451:1: E302 [*] Expected 2 blank lines, found 0 + | +449 | def bar(): +450 | pass +451 | def baz(): pass | ^^^ E302 -436 | # end +452 | # end | = help: Add missing blank line(s) ℹ Safe fix -432 432 | # E302 -433 433 | def bar(): -434 434 | pass - 435 |+ - 436 |+ -435 437 | def baz(): pass -436 438 | # end -437 439 | - -E30.py:441:1: E302 [*] Expected 2 blank lines, found 0 - | -439 | # E302 -440 | def bar(): pass -441 | def baz(): +448 448 | # E302 +449 449 | def bar(): +450 450 | pass + 451 |+ + 452 |+ +451 453 | def baz(): pass +452 454 | # end +453 455 | + +E30.py:457:1: E302 [*] Expected 2 blank lines, found 0 + | +455 | # E302 +456 | def bar(): pass +457 | def baz(): | ^^^ E302 -442 | pass -443 | # end +458 | pass +459 | # end | = help: Add missing blank line(s) ℹ Safe fix -438 438 | -439 439 | # E302 -440 440 | def bar(): pass - 441 |+ - 442 |+ -441 443 | def baz(): -442 444 | pass -443 445 | # end - -E30.py:451:1: E302 [*] Expected 2 blank lines, found 1 - | -450 | # comment -451 | @decorator +454 454 | +455 455 | # E302 +456 456 | def bar(): pass + 457 |+ + 458 |+ +457 459 | def baz(): +458 460 | pass +459 461 | # end + +E30.py:467:1: E302 [*] Expected 2 blank lines, found 1 + | +466 | # comment +467 | @decorator | ^ E302 -452 | def g(): -453 | pass +468 | def g(): +469 | pass | = help: Add missing blank line(s) ℹ Safe fix -447 447 | def f(): -448 448 | pass -449 449 | - 450 |+ -450 451 | # comment -451 452 | @decorator -452 453 | def g(): +463 463 | def f(): +464 464 | pass +465 465 | + 466 |+ +466 467 | # comment +467 468 | @decorator +468 469 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index d20f62dd570ea..3a3fdbfe6e75c 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:462:5: E303 [*] Too many blank lines (2) +E30.py:478:5: E303 [*] Too many blank lines (2) | -462 | # arbitrary comment +478 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -463 | -464 | def inner(): # E306 not expected +479 | +480 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -458 458 | def fn(): -459 459 | _ = None -460 460 | -461 |- -462 461 | # arbitrary comment -463 462 | -464 463 | def inner(): # E306 not expected +474 474 | def fn(): +475 475 | _ = None +476 476 | +477 |- +478 477 | # arbitrary comment +479 478 | +480 479 | def inner(): # E306 not expected -E30.py:474:5: E303 [*] Too many blank lines (2) +E30.py:490:5: E303 [*] Too many blank lines (2) | -474 | # arbitrary comment +490 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -475 | def inner(): # E306 not expected -476 | pass +491 | def inner(): # E306 not expected +492 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -470 470 | def fn(): -471 471 | _ = None -472 472 | -473 |- -474 473 | # arbitrary comment -475 474 | def inner(): # E306 not expected -476 475 | pass +486 486 | def fn(): +487 487 | _ = None +488 488 | +489 |- +490 489 | # arbitrary comment +491 490 | def inner(): # E306 not expected +492 491 | pass -E30.py:485:1: E303 [*] Too many blank lines (3) +E30.py:501:1: E303 [*] Too many blank lines (3) | -485 | print() +501 | print() | ^^^^^ E303 -486 | # end +502 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -481 481 | print() -482 482 | -483 483 | -484 |- -485 484 | print() -486 485 | # end -487 486 | +497 497 | print() +498 498 | +499 499 | +500 |- +501 500 | print() +502 501 | # end +503 502 | -E30.py:494:1: E303 [*] Too many blank lines (3) +E30.py:510:1: E303 [*] Too many blank lines (3) | -494 | # comment +510 | # comment | ^^^^^^^^^ E303 -495 | -496 | print() +511 | +512 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -490 490 | print() -491 491 | -492 492 | -493 |- -494 493 | # comment -495 494 | -496 495 | print() +506 506 | print() +507 507 | +508 508 | +509 |- +510 509 | # comment +511 510 | +512 511 | print() -E30.py:505:5: E303 [*] Too many blank lines (2) +E30.py:521:5: E303 [*] Too many blank lines (2) | -505 | # comment +521 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -501 501 | def a(): -502 502 | print() -503 503 | -504 |- -505 504 | # comment -506 505 | -507 506 | +517 517 | def a(): +518 518 | print() +519 519 | +520 |- +521 520 | # comment +522 521 | +523 522 | -E30.py:508:5: E303 [*] Too many blank lines (2) +E30.py:524:5: E303 [*] Too many blank lines (2) | -508 | # another comment +524 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -509 | -510 | print() +525 | +526 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -504 504 | -505 505 | # comment -506 506 | -507 |- -508 507 | # another comment -509 508 | -510 509 | print() - -E30.py:519:1: E303 [*] Too many blank lines (3) - | -519 | / """This class docstring comes on line 5. -520 | | It gives error E303: too many blank lines (3) -521 | | """ +520 520 | +521 521 | # comment +522 522 | +523 |- +524 523 | # another comment +525 524 | +526 525 | print() + +E30.py:535:1: E303 [*] Too many blank lines (3) + | +535 | / """This class docstring comes on line 5. +536 | | It gives error E303: too many blank lines (3) +537 | | """ | |___^ E303 -522 | # end +538 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -515 515 | #!python -516 516 | -517 517 | -518 |- -519 518 | """This class docstring comes on line 5. -520 519 | It gives error E303: too many blank lines (3) -521 520 | """ +531 531 | #!python +532 532 | +533 533 | +534 |- +535 534 | """This class docstring comes on line 5. +536 535 | It gives error E303: too many blank lines (3) +537 536 | """ -E30.py:531:5: E303 [*] Too many blank lines (2) +E30.py:547:5: E303 [*] Too many blank lines (2) | -531 | def b(self): +547 | def b(self): | ^^^ E303 -532 | pass -533 | # end +548 | pass +549 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -527 527 | def a(self): -528 528 | pass -529 529 | -530 |- -531 530 | def b(self): -532 531 | pass -533 532 | # end +543 543 | def a(self): +544 544 | pass +545 545 | +546 |- +547 546 | def b(self): +548 547 | pass +549 548 | # end -E30.py:541:5: E303 [*] Too many blank lines (2) +E30.py:557:5: E303 [*] Too many blank lines (2) | -541 | a = 2 +557 | a = 2 | ^ E303 -542 | # end +558 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -537 537 | if True: -538 538 | a = 1 -539 539 | -540 |- -541 540 | a = 2 -542 541 | # end -543 542 | +553 553 | if True: +554 554 | a = 1 +555 555 | +556 |- +557 556 | a = 2 +558 557 | # end +559 558 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 77a17561b882d..cf0710bf096c7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:548:1: E304 [*] blank lines found after function decorator +E30.py:564:1: E304 [*] blank lines found after function decorator | -546 | @decorator -547 | -548 | def function(): +562 | @decorator +563 | +564 | def function(): | ^^^ E304 -549 | pass -550 | # end +565 | pass +566 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -544 544 | -545 545 | # E304 -546 546 | @decorator -547 |- -548 547 | def function(): -549 548 | pass -550 549 | # end +560 560 | +561 561 | # E304 +562 562 | @decorator +563 |- +564 563 | def function(): +565 564 | pass +566 565 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 6fc6b93e89781..3cf9f303930ae 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:560:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:576:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -559 | # another comment -560 | fn() +575 | # another comment +576 | fn() | ^^ E305 -561 | # end +577 | # end | = help: Add missing blank line(s) ℹ Safe fix -557 557 | # comment -558 558 | -559 559 | # another comment - 560 |+ - 561 |+ -560 562 | fn() -561 563 | # end -562 564 | +573 573 | # comment +574 574 | +575 575 | # another comment + 576 |+ + 577 |+ +576 578 | fn() +577 579 | # end +578 580 | -E30.py:571:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:587:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -570 | # another comment -571 | a = 1 +586 | # another comment +587 | a = 1 | ^ E305 -572 | # end +588 | # end | = help: Add missing blank line(s) ℹ Safe fix -568 568 | # comment -569 569 | -570 570 | # another comment - 571 |+ - 572 |+ -571 573 | a = 1 -572 574 | # end -573 575 | +584 584 | # comment +585 585 | +586 586 | # another comment + 587 |+ + 588 |+ +587 589 | a = 1 +588 590 | # end +589 591 | -E30.py:583:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:599:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -581 | # another comment -582 | -583 | try: +597 | # another comment +598 | +599 | try: | ^^^ E305 -584 | fn() -585 | except Exception: +600 | fn() +601 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -580 580 | -581 581 | # another comment -582 582 | - 583 |+ -583 584 | try: -584 585 | fn() -585 586 | except Exception: +596 596 | +597 597 | # another comment +598 598 | + 599 |+ +599 600 | try: +600 601 | fn() +601 602 | except Exception: -E30.py:595:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:611:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -594 | # Two spaces before comments, too. -595 | if a(): +610 | # Two spaces before comments, too. +611 | if a(): | ^^ E305 -596 | a() -597 | # end +612 | a() +613 | # end | = help: Add missing blank line(s) ℹ Safe fix -592 592 | print -593 593 | -594 594 | # Two spaces before comments, too. - 595 |+ - 596 |+ -595 597 | if a(): -596 598 | a() -597 599 | # end +608 608 | print +609 609 | +610 610 | # Two spaces before comments, too. + 611 |+ + 612 |+ +611 613 | if a(): +612 614 | a() +613 615 | # end -E30.py:608:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:624:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -606 | blah, blah -607 | -608 | if __name__ == '__main__': +622 | blah, blah +623 | +624 | if __name__ == '__main__': | ^^ E305 -609 | main() -610 | # end +625 | main() +626 | # end | = help: Add missing blank line(s) ℹ Safe fix -605 605 | def main(): -606 606 | blah, blah -607 607 | - 608 |+ -608 609 | if __name__ == '__main__': -609 610 | main() -610 611 | # end +621 621 | def main(): +622 622 | blah, blah +623 623 | + 624 |+ +624 625 | if __name__ == '__main__': +625 626 | main() +626 627 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 3b69bf5ca4be6..e512a163cfdf5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,150 +1,111 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:616:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -614 | def a(): -615 | x = 1 -616 | def b(): - | ^^^ E306 -617 | pass -618 | # end - | - = help: Add missing blank line - -ℹ Safe fix -613 613 | # E306:3:5 -614 614 | def a(): -615 615 | x = 1 - 616 |+ -616 617 | def b(): -617 618 | pass -618 619 | # end - -E30.py:624:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -622 | async def a(): -623 | x = 1 -624 | def b(): - | ^^^ E306 -625 | pass -626 | # end - | - = help: Add missing blank line - -ℹ Safe fix -621 621 | #: E306:3:5 -622 622 | async def a(): -623 623 | x = 1 - 624 |+ -624 625 | def b(): -625 626 | pass -626 627 | # end - E30.py:632:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | 630 | def a(): -631 | x = 2 +631 | x = 1 632 | def b(): | ^^^ E306 -633 | x = 1 -634 | def c(): +633 | pass +634 | # end | = help: Add missing blank line ℹ Safe fix -629 629 | #: E306:3:5 E306:5:9 +629 629 | # E306:3:5 630 630 | def a(): -631 631 | x = 2 +631 631 | x = 1 632 |+ 632 633 | def b(): -633 634 | x = 1 -634 635 | def c(): +633 634 | pass +634 635 | # end -E30.py:634:9: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:640:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -632 | def b(): -633 | x = 1 -634 | def c(): - | ^^^ E306 -635 | pass -636 | # end +638 | async def a(): +639 | x = 1 +640 | def b(): + | ^^^ E306 +641 | pass +642 | # end | = help: Add missing blank line ℹ Safe fix -631 631 | x = 2 -632 632 | def b(): -633 633 | x = 1 - 634 |+ -634 635 | def c(): -635 636 | pass -636 637 | # end - -E30.py:642:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -640 | def a(): -641 | x = 1 -642 | class C: - | ^^^^^ E306 -643 | pass -644 | x = 2 +637 637 | #: E306:3:5 +638 638 | async def a(): +639 639 | x = 1 + 640 |+ +640 641 | def b(): +641 642 | pass +642 643 | # end + +E30.py:648:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +646 | def a(): +647 | x = 2 +648 | def b(): + | ^^^ E306 +649 | x = 1 +650 | def c(): | = help: Add missing blank line ℹ Safe fix -639 639 | # E306:3:5 E306:6:5 -640 640 | def a(): -641 641 | x = 1 - 642 |+ -642 643 | class C: -643 644 | pass -644 645 | x = 2 - -E30.py:645:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -643 | pass -644 | x = 2 -645 | def b(): - | ^^^ E306 -646 | pass -647 | # end +645 645 | #: E306:3:5 E306:5:9 +646 646 | def a(): +647 647 | x = 2 + 648 |+ +648 649 | def b(): +649 650 | x = 1 +650 651 | def c(): + +E30.py:650:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +648 | def b(): +649 | x = 1 +650 | def c(): + | ^^^ E306 +651 | pass +652 | # end | = help: Add missing blank line ℹ Safe fix -642 642 | class C: -643 643 | pass -644 644 | x = 2 - 645 |+ -645 646 | def b(): -646 647 | pass -647 648 | # end - -E30.py:654:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -652 | def bar(): -653 | pass -654 | def baz(): pass - | ^^^ E306 -655 | # end +647 647 | x = 2 +648 648 | def b(): +649 649 | x = 1 + 650 |+ +650 651 | def c(): +651 652 | pass +652 653 | # end + +E30.py:658:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +656 | def a(): +657 | x = 1 +658 | class C: + | ^^^^^ E306 +659 | pass +660 | x = 2 | = help: Add missing blank line ℹ Safe fix -651 651 | def foo(): -652 652 | def bar(): -653 653 | pass - 654 |+ -654 655 | def baz(): pass -655 656 | # end -656 657 | +655 655 | # E306:3:5 E306:6:5 +656 656 | def a(): +657 657 | x = 1 + 658 |+ +658 659 | class C: +659 660 | pass +660 661 | x = 2 E30.py:661:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -659 | def foo(): -660 | def bar(): pass -661 | def baz(): +659 | pass +660 | x = 2 +661 | def b(): | ^^^ E306 662 | pass 663 | # end @@ -152,72 +113,111 @@ E30.py:661:5: E306 [*] Expected 1 blank line before a nested definition, found 0 = help: Add missing blank line ℹ Safe fix -658 658 | # E306:3:5 -659 659 | def foo(): -660 660 | def bar(): pass +658 658 | class C: +659 659 | pass +660 660 | x = 2 661 |+ -661 662 | def baz(): +661 662 | def b(): 662 663 | pass 663 664 | # end -E30.py:669:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:670:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +668 | def bar(): +669 | pass +670 | def baz(): pass + | ^^^ E306 +671 | # end | -667 | def a(): -668 | x = 2 -669 | @decorator + = help: Add missing blank line + +ℹ Safe fix +667 667 | def foo(): +668 668 | def bar(): +669 669 | pass + 670 |+ +670 671 | def baz(): pass +671 672 | # end +672 673 | + +E30.py:677:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +675 | def foo(): +676 | def bar(): pass +677 | def baz(): + | ^^^ E306 +678 | pass +679 | # end + | + = help: Add missing blank line + +ℹ Safe fix +674 674 | # E306:3:5 +675 675 | def foo(): +676 676 | def bar(): pass + 677 |+ +677 678 | def baz(): +678 679 | pass +679 680 | # end + +E30.py:685:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +683 | def a(): +684 | x = 2 +685 | @decorator | ^ E306 -670 | def b(): -671 | pass +686 | def b(): +687 | pass | = help: Add missing blank line ℹ Safe fix -666 666 | # E306 -667 667 | def a(): -668 668 | x = 2 - 669 |+ -669 670 | @decorator -670 671 | def b(): -671 672 | pass - -E30.py:678:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -676 | def a(): -677 | x = 2 -678 | @decorator +682 682 | # E306 +683 683 | def a(): +684 684 | x = 2 + 685 |+ +685 686 | @decorator +686 687 | def b(): +687 688 | pass + +E30.py:694:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +692 | def a(): +693 | x = 2 +694 | @decorator | ^ E306 -679 | async def b(): -680 | pass +695 | async def b(): +696 | pass | = help: Add missing blank line ℹ Safe fix -675 675 | # E306 -676 676 | def a(): -677 677 | x = 2 - 678 |+ -678 679 | @decorator -679 680 | async def b(): -680 681 | pass - -E30.py:687:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -685 | def a(): -686 | x = 2 -687 | async def b(): +691 691 | # E306 +692 692 | def a(): +693 693 | x = 2 + 694 |+ +694 695 | @decorator +695 696 | async def b(): +696 697 | pass + +E30.py:703:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +701 | def a(): +702 | x = 2 +703 | async def b(): | ^^^^^ E306 -688 | pass -689 | # end +704 | pass +705 | # end | = help: Add missing blank line ℹ Safe fix -684 684 | # E306 -685 685 | def a(): -686 686 | x = 2 - 687 |+ -687 688 | async def b(): -688 689 | pass -689 690 | # end +700 700 | # E306 +701 701 | def a(): +702 702 | x = 2 + 703 |+ +703 704 | async def b(): +704 705 | pass +705 706 | # end From 66f4dd4de86a58380903f9252ae96fb0f9a0edef Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 29 Dec 2023 12:51:33 +0900 Subject: [PATCH 063/122] Fix false positive caused by bad async handling. --- .../test/fixtures/pycodestyle/E30.py | 8 + .../rules/pycodestyle/rules/blank_lines.rs | 22 +- ...ules__pycodestyle__tests__E301_E30.py.snap | 52 ++-- ...ules__pycodestyle__tests__E302_E30.py.snap | 266 ++++++++--------- ...ules__pycodestyle__tests__E303_E30.py.snap | 196 ++++++------- ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 ++++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 268 +++++++++--------- 8 files changed, 489 insertions(+), 475 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index a4435efaf0d19..88040a65a2713 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -372,6 +372,14 @@ def function2(): # end +# no error +async def function1(): + await function2() + async with function3(): + pass +# end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 1b9022854fdc3..11f191e45e3fe 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -378,7 +378,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { let mut line_is_comment_only = true; let mut is_docstring = true; let mut first_token: Option = None; - let mut first_token_range = TextRange::new(0.into(), 0.into()); + let mut first_token_range: Option = None; let mut last_token: Option = None; let mut parens = 0u32; @@ -388,9 +388,13 @@ impl<'a> Iterator for LinePreprocessor<'a> { } if !matches!(token, Tok::Newline) { - if first_token.is_none() { + // Ideally, we would like to have a "async def" token since we care about the "def" part. + // As a work around, we ignore the first token if it is "async". + if first_token.is_none() && !matches!(token, Tok::Async) { first_token = Some(token.clone()); - first_token_range = *range; + } + if first_token_range.is_none() { + first_token_range = Some(*range); } if !matches!(token, Tok::NonLogicalNewline) { @@ -421,12 +425,14 @@ impl<'a> Iterator for LinePreprocessor<'a> { is_docstring = false; } + let first_range = first_token_range.unwrap(); + let range = if matches!(first_token, Some(Tok::Indent)) { - first_token_range + first_range } else { TextRange::new( - self.locator.line_start(first_token_range.start()), - first_token_range.start(), + self.locator.line_start(first_range.start()), + first_range.start(), ) }; let indent_level = expand_indent(self.locator.slice(range)); @@ -435,7 +441,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { if matches!(first_token, Some(Tok::NonLogicalNewline)) { self.current_blank_lines += 1; self.current_blank_characters += - range.end() - first_token_range.start() + TextSize::new(1); + range.end() - first_range.start() + TextSize::new(1); return self.next(); } @@ -445,7 +451,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { let logical_line = LogicalLineInfo { first_token: first_token.clone().unwrap(), - first_token_range, + first_token_range: first_range, last_token: last_token.unwrap(), last_token_range, is_comment_only: line_is_comment_only, diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index 66514671df460..c7cd64c2457a3 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:380:5: E301 [*] Expected 1 blank line, found 0 +E30.py:388:5: E301 [*] Expected 1 blank line, found 0 | -378 | def func1(): -379 | pass -380 | def func2(): +386 | def func1(): +387 | pass +388 | def func2(): | ^^^ E301 -381 | pass -382 | # end +389 | pass +390 | # end | = help: Add missing blank line(s) ℹ Safe fix -377 377 | -378 378 | def func1(): -379 379 | pass - 380 |+ -380 381 | def func2(): -381 382 | pass -382 383 | # end +385 385 | +386 386 | def func1(): +387 387 | pass + 388 |+ +388 389 | def func2(): +389 390 | pass +390 391 | # end -E30.py:391:5: E301 [*] Expected 1 blank line, found 0 +E30.py:399:5: E301 [*] Expected 1 blank line, found 0 | -389 | pass -390 | # comment -391 | def fn2(): +397 | pass +398 | # comment +399 | def fn2(): | ^^^ E301 -392 | pass -393 | # end +400 | pass +401 | # end | = help: Add missing blank line(s) ℹ Safe fix -387 387 | -388 388 | def fn1(): -389 389 | pass - 390 |+ -390 391 | # comment -391 392 | def fn2(): -392 393 | pass +395 395 | +396 396 | def fn1(): +397 397 | pass + 398 |+ +398 399 | # comment +399 400 | def fn2(): +400 401 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 85175ee186825..341ee4e9a2239 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,186 +1,186 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:398:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:406:1: E302 [*] Expected 2 blank lines, found 0 | -396 | # E302 -397 | """Main module.""" -398 | def fn(): +404 | # E302 +405 | """Main module.""" +406 | def fn(): | ^^^ E302 -399 | pass -400 | # end +407 | pass +408 | # end | = help: Add missing blank line(s) ℹ Safe fix -395 395 | -396 396 | # E302 -397 397 | """Main module.""" - 398 |+ - 399 |+ -398 400 | def fn(): -399 401 | pass -400 402 | # end - -E30.py:405:1: E302 [*] Expected 2 blank lines, found 0 - | -403 | # E302 -404 | import sys -405 | def get_sys_path(): - | ^^^ E302 -406 | return sys.path -407 | # end - | - = help: Add missing blank line(s) - -ℹ Safe fix -402 402 | -403 403 | # E302 -404 404 | import sys - 405 |+ +403 403 | +404 404 | # E302 +405 405 | """Main module.""" 406 |+ -405 407 | def get_sys_path(): -406 408 | return sys.path -407 409 | # end + 407 |+ +406 408 | def fn(): +407 409 | pass +408 410 | # end -E30.py:414:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:413:1: E302 [*] Expected 2 blank lines, found 0 | -412 | pass -413 | -414 | def b(): +411 | # E302 +412 | import sys +413 | def get_sys_path(): | ^^^ E302 -415 | pass -416 | # end +414 | return sys.path +415 | # end | = help: Add missing blank line(s) ℹ Safe fix -411 411 | def a(): -412 412 | pass -413 413 | +410 410 | +411 411 | # E302 +412 412 | import sys + 413 |+ 414 |+ -414 415 | def b(): -415 416 | pass -416 417 | # end +413 415 | def get_sys_path(): +414 416 | return sys.path +415 417 | # end -E30.py:425:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:422:1: E302 [*] Expected 2 blank lines, found 1 | -423 | # comment -424 | -425 | def b(): +420 | pass +421 | +422 | def b(): | ^^^ E302 -426 | pass -427 | # end +423 | pass +424 | # end | = help: Add missing blank line(s) ℹ Safe fix -420 420 | def a(): -421 421 | pass -422 422 | - 423 |+ -423 424 | # comment -424 425 | -425 426 | def b(): - -E30.py:434:1: E302 [*] Expected 2 blank lines, found 1 - | -432 | pass -433 | -434 | async def b(): - | ^^^^^ E302 -435 | pass -436 | # end +419 419 | def a(): +420 420 | pass +421 421 | + 422 |+ +422 423 | def b(): +423 424 | pass +424 425 | # end + +E30.py:433:1: E302 [*] Expected 2 blank lines, found 1 + | +431 | # comment +432 | +433 | def b(): + | ^^^ E302 +434 | pass +435 | # end | = help: Add missing blank line(s) ℹ Safe fix -431 431 | def a(): -432 432 | pass -433 433 | - 434 |+ -434 435 | async def b(): -435 436 | pass -436 437 | # end - -E30.py:443:1: E302 [*] Expected 2 blank lines, found 1 - | -441 | pass -442 | -443 | async def x(y: int = 1): +428 428 | def a(): +429 429 | pass +430 430 | + 431 |+ +431 432 | # comment +432 433 | +433 434 | def b(): + +E30.py:442:1: E302 [*] Expected 2 blank lines, found 1 + | +440 | pass +441 | +442 | async def b(): | ^^^^^ E302 -444 | pass -445 | # end +443 | pass +444 | # end | = help: Add missing blank line(s) ℹ Safe fix -440 440 | async def x(): -441 441 | pass -442 442 | - 443 |+ -443 444 | async def x(y: int = 1): -444 445 | pass -445 446 | # end - -E30.py:451:1: E302 [*] Expected 2 blank lines, found 0 - | -449 | def bar(): -450 | pass -451 | def baz(): pass - | ^^^ E302 -452 | # end +439 439 | def a(): +440 440 | pass +441 441 | + 442 |+ +442 443 | async def b(): +443 444 | pass +444 445 | # end + +E30.py:451:1: E302 [*] Expected 2 blank lines, found 1 + | +449 | pass +450 | +451 | async def x(y: int = 1): + | ^^^^^ E302 +452 | pass +453 | # end | = help: Add missing blank line(s) ℹ Safe fix -448 448 | # E302 -449 449 | def bar(): -450 450 | pass +448 448 | async def x(): +449 449 | pass +450 450 | 451 |+ - 452 |+ -451 453 | def baz(): pass -452 454 | # end -453 455 | +451 452 | async def x(y: int = 1): +452 453 | pass +453 454 | # end -E30.py:457:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:459:1: E302 [*] Expected 2 blank lines, found 0 | -455 | # E302 -456 | def bar(): pass -457 | def baz(): - | ^^^ E302 +457 | def bar(): 458 | pass -459 | # end +459 | def baz(): pass + | ^^^ E302 +460 | # end | = help: Add missing blank line(s) ℹ Safe fix -454 454 | -455 455 | # E302 -456 456 | def bar(): pass - 457 |+ - 458 |+ -457 459 | def baz(): -458 460 | pass -459 461 | # end - -E30.py:467:1: E302 [*] Expected 2 blank lines, found 1 - | -466 | # comment -467 | @decorator - | ^ E302 -468 | def g(): -469 | pass +456 456 | # E302 +457 457 | def bar(): +458 458 | pass + 459 |+ + 460 |+ +459 461 | def baz(): pass +460 462 | # end +461 463 | + +E30.py:465:1: E302 [*] Expected 2 blank lines, found 0 + | +463 | # E302 +464 | def bar(): pass +465 | def baz(): + | ^^^ E302 +466 | pass +467 | # end | = help: Add missing blank line(s) ℹ Safe fix -463 463 | def f(): -464 464 | pass -465 465 | +462 462 | +463 463 | # E302 +464 464 | def bar(): pass + 465 |+ 466 |+ -466 467 | # comment -467 468 | @decorator -468 469 | def g(): +465 467 | def baz(): +466 468 | pass +467 469 | # end + +E30.py:475:1: E302 [*] Expected 2 blank lines, found 1 + | +474 | # comment +475 | @decorator + | ^ E302 +476 | def g(): +477 | pass + | + = help: Add missing blank line(s) + +ℹ Safe fix +471 471 | def f(): +472 472 | pass +473 473 | + 474 |+ +474 475 | # comment +475 476 | @decorator +476 477 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 3a3fdbfe6e75c..0be66fb4d63d7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:478:5: E303 [*] Too many blank lines (2) +E30.py:486:5: E303 [*] Too many blank lines (2) | -478 | # arbitrary comment +486 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -479 | -480 | def inner(): # E306 not expected +487 | +488 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -474 474 | def fn(): -475 475 | _ = None -476 476 | -477 |- -478 477 | # arbitrary comment -479 478 | -480 479 | def inner(): # E306 not expected +482 482 | def fn(): +483 483 | _ = None +484 484 | +485 |- +486 485 | # arbitrary comment +487 486 | +488 487 | def inner(): # E306 not expected -E30.py:490:5: E303 [*] Too many blank lines (2) +E30.py:498:5: E303 [*] Too many blank lines (2) | -490 | # arbitrary comment +498 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -491 | def inner(): # E306 not expected -492 | pass +499 | def inner(): # E306 not expected +500 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -486 486 | def fn(): -487 487 | _ = None -488 488 | -489 |- -490 489 | # arbitrary comment -491 490 | def inner(): # E306 not expected -492 491 | pass +494 494 | def fn(): +495 495 | _ = None +496 496 | +497 |- +498 497 | # arbitrary comment +499 498 | def inner(): # E306 not expected +500 499 | pass -E30.py:501:1: E303 [*] Too many blank lines (3) +E30.py:509:1: E303 [*] Too many blank lines (3) | -501 | print() +509 | print() | ^^^^^ E303 -502 | # end +510 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -497 497 | print() -498 498 | -499 499 | -500 |- -501 500 | print() -502 501 | # end -503 502 | +505 505 | print() +506 506 | +507 507 | +508 |- +509 508 | print() +510 509 | # end +511 510 | -E30.py:510:1: E303 [*] Too many blank lines (3) +E30.py:518:1: E303 [*] Too many blank lines (3) | -510 | # comment +518 | # comment | ^^^^^^^^^ E303 -511 | -512 | print() +519 | +520 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -506 506 | print() -507 507 | -508 508 | -509 |- -510 509 | # comment -511 510 | -512 511 | print() +514 514 | print() +515 515 | +516 516 | +517 |- +518 517 | # comment +519 518 | +520 519 | print() -E30.py:521:5: E303 [*] Too many blank lines (2) +E30.py:529:5: E303 [*] Too many blank lines (2) | -521 | # comment +529 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -517 517 | def a(): -518 518 | print() -519 519 | -520 |- -521 520 | # comment -522 521 | -523 522 | +525 525 | def a(): +526 526 | print() +527 527 | +528 |- +529 528 | # comment +530 529 | +531 530 | -E30.py:524:5: E303 [*] Too many blank lines (2) +E30.py:532:5: E303 [*] Too many blank lines (2) | -524 | # another comment +532 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -525 | -526 | print() +533 | +534 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -520 520 | -521 521 | # comment -522 522 | -523 |- -524 523 | # another comment -525 524 | -526 525 | print() - -E30.py:535:1: E303 [*] Too many blank lines (3) - | -535 | / """This class docstring comes on line 5. -536 | | It gives error E303: too many blank lines (3) -537 | | """ +528 528 | +529 529 | # comment +530 530 | +531 |- +532 531 | # another comment +533 532 | +534 533 | print() + +E30.py:543:1: E303 [*] Too many blank lines (3) + | +543 | / """This class docstring comes on line 5. +544 | | It gives error E303: too many blank lines (3) +545 | | """ | |___^ E303 -538 | # end +546 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -531 531 | #!python -532 532 | -533 533 | -534 |- -535 534 | """This class docstring comes on line 5. -536 535 | It gives error E303: too many blank lines (3) -537 536 | """ +539 539 | #!python +540 540 | +541 541 | +542 |- +543 542 | """This class docstring comes on line 5. +544 543 | It gives error E303: too many blank lines (3) +545 544 | """ -E30.py:547:5: E303 [*] Too many blank lines (2) +E30.py:555:5: E303 [*] Too many blank lines (2) | -547 | def b(self): +555 | def b(self): | ^^^ E303 -548 | pass -549 | # end +556 | pass +557 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -543 543 | def a(self): -544 544 | pass -545 545 | -546 |- -547 546 | def b(self): -548 547 | pass -549 548 | # end +551 551 | def a(self): +552 552 | pass +553 553 | +554 |- +555 554 | def b(self): +556 555 | pass +557 556 | # end -E30.py:557:5: E303 [*] Too many blank lines (2) +E30.py:565:5: E303 [*] Too many blank lines (2) | -557 | a = 2 +565 | a = 2 | ^ E303 -558 | # end +566 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -553 553 | if True: -554 554 | a = 1 -555 555 | -556 |- -557 556 | a = 2 -558 557 | # end -559 558 | +561 561 | if True: +562 562 | a = 1 +563 563 | +564 |- +565 564 | a = 2 +566 565 | # end +567 566 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index cf0710bf096c7..91280c8f8896f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:564:1: E304 [*] blank lines found after function decorator +E30.py:572:1: E304 [*] blank lines found after function decorator | -562 | @decorator -563 | -564 | def function(): +570 | @decorator +571 | +572 | def function(): | ^^^ E304 -565 | pass -566 | # end +573 | pass +574 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -560 560 | -561 561 | # E304 -562 562 | @decorator -563 |- -564 563 | def function(): -565 564 | pass -566 565 | # end +568 568 | +569 569 | # E304 +570 570 | @decorator +571 |- +572 571 | def function(): +573 572 | pass +574 573 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 3cf9f303930ae..74b881ba5dc45 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:576:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:584:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -575 | # another comment -576 | fn() +583 | # another comment +584 | fn() | ^^ E305 -577 | # end +585 | # end | = help: Add missing blank line(s) ℹ Safe fix -573 573 | # comment -574 574 | -575 575 | # another comment - 576 |+ - 577 |+ -576 578 | fn() -577 579 | # end -578 580 | +581 581 | # comment +582 582 | +583 583 | # another comment + 584 |+ + 585 |+ +584 586 | fn() +585 587 | # end +586 588 | -E30.py:587:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:595:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -586 | # another comment -587 | a = 1 +594 | # another comment +595 | a = 1 | ^ E305 -588 | # end +596 | # end | = help: Add missing blank line(s) ℹ Safe fix -584 584 | # comment -585 585 | -586 586 | # another comment - 587 |+ - 588 |+ -587 589 | a = 1 -588 590 | # end -589 591 | +592 592 | # comment +593 593 | +594 594 | # another comment + 595 |+ + 596 |+ +595 597 | a = 1 +596 598 | # end +597 599 | -E30.py:599:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:607:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -597 | # another comment -598 | -599 | try: +605 | # another comment +606 | +607 | try: | ^^^ E305 -600 | fn() -601 | except Exception: +608 | fn() +609 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -596 596 | -597 597 | # another comment -598 598 | - 599 |+ -599 600 | try: -600 601 | fn() -601 602 | except Exception: +604 604 | +605 605 | # another comment +606 606 | + 607 |+ +607 608 | try: +608 609 | fn() +609 610 | except Exception: -E30.py:611:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:619:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -610 | # Two spaces before comments, too. -611 | if a(): +618 | # Two spaces before comments, too. +619 | if a(): | ^^ E305 -612 | a() -613 | # end +620 | a() +621 | # end | = help: Add missing blank line(s) ℹ Safe fix -608 608 | print -609 609 | -610 610 | # Two spaces before comments, too. - 611 |+ - 612 |+ -611 613 | if a(): -612 614 | a() -613 615 | # end +616 616 | print +617 617 | +618 618 | # Two spaces before comments, too. + 619 |+ + 620 |+ +619 621 | if a(): +620 622 | a() +621 623 | # end -E30.py:624:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:632:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -622 | blah, blah -623 | -624 | if __name__ == '__main__': +630 | blah, blah +631 | +632 | if __name__ == '__main__': | ^^ E305 -625 | main() -626 | # end +633 | main() +634 | # end | = help: Add missing blank line(s) ℹ Safe fix -621 621 | def main(): -622 622 | blah, blah -623 623 | - 624 |+ -624 625 | if __name__ == '__main__': -625 626 | main() -626 627 | # end +629 629 | def main(): +630 630 | blah, blah +631 631 | + 632 |+ +632 633 | if __name__ == '__main__': +633 634 | main() +634 635 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index e512a163cfdf5..7955b12f2947f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,29 +1,9 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:632:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -630 | def a(): -631 | x = 1 -632 | def b(): - | ^^^ E306 -633 | pass -634 | # end - | - = help: Add missing blank line - -ℹ Safe fix -629 629 | # E306:3:5 -630 630 | def a(): -631 631 | x = 1 - 632 |+ -632 633 | def b(): -633 634 | pass -634 635 | # end - E30.py:640:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -638 | async def a(): +638 | def a(): 639 | x = 1 640 | def b(): | ^^^ E306 @@ -33,8 +13,8 @@ E30.py:640:5: E306 [*] Expected 1 blank line before a nested definition, found 0 = help: Add missing blank line ℹ Safe fix -637 637 | #: E306:3:5 -638 638 | async def a(): +637 637 | # E306:3:5 +638 638 | def a(): 639 639 | x = 1 640 |+ 640 641 | def b(): @@ -43,181 +23,201 @@ E30.py:640:5: E306 [*] Expected 1 blank line before a nested definition, found 0 E30.py:648:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -646 | def a(): -647 | x = 2 +646 | async def a(): +647 | x = 1 648 | def b(): | ^^^ E306 -649 | x = 1 -650 | def c(): +649 | pass +650 | # end | = help: Add missing blank line ℹ Safe fix -645 645 | #: E306:3:5 E306:5:9 -646 646 | def a(): -647 647 | x = 2 +645 645 | #: E306:3:5 +646 646 | async def a(): +647 647 | x = 1 648 |+ 648 649 | def b(): -649 650 | x = 1 -650 651 | def c(): +649 650 | pass +650 651 | # end -E30.py:650:9: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:656:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -648 | def b(): -649 | x = 1 -650 | def c(): - | ^^^ E306 -651 | pass -652 | # end +654 | def a(): +655 | x = 2 +656 | def b(): + | ^^^ E306 +657 | x = 1 +658 | def c(): | = help: Add missing blank line ℹ Safe fix -647 647 | x = 2 -648 648 | def b(): -649 649 | x = 1 - 650 |+ -650 651 | def c(): -651 652 | pass -652 653 | # end - -E30.py:658:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -656 | def a(): -657 | x = 1 -658 | class C: - | ^^^^^ E306 -659 | pass -660 | x = 2 +653 653 | #: E306:3:5 E306:5:9 +654 654 | def a(): +655 655 | x = 2 + 656 |+ +656 657 | def b(): +657 658 | x = 1 +658 659 | def c(): + +E30.py:658:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +656 | def b(): +657 | x = 1 +658 | def c(): + | ^^^ E306 +659 | pass +660 | # end | = help: Add missing blank line ℹ Safe fix -655 655 | # E306:3:5 E306:6:5 -656 656 | def a(): -657 657 | x = 1 +655 655 | x = 2 +656 656 | def b(): +657 657 | x = 1 658 |+ -658 659 | class C: -659 660 | pass -660 661 | x = 2 +658 659 | def c(): +659 660 | pass +660 661 | # end -E30.py:661:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:666:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -659 | pass -660 | x = 2 -661 | def b(): - | ^^^ E306 -662 | pass -663 | # end +664 | def a(): +665 | x = 1 +666 | class C: + | ^^^^^ E306 +667 | pass +668 | x = 2 | = help: Add missing blank line ℹ Safe fix -658 658 | class C: -659 659 | pass -660 660 | x = 2 - 661 |+ -661 662 | def b(): -662 663 | pass -663 664 | # end - -E30.py:670:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -668 | def bar(): -669 | pass -670 | def baz(): pass +663 663 | # E306:3:5 E306:6:5 +664 664 | def a(): +665 665 | x = 1 + 666 |+ +666 667 | class C: +667 668 | pass +668 669 | x = 2 + +E30.py:669:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +667 | pass +668 | x = 2 +669 | def b(): | ^^^ E306 +670 | pass 671 | # end | = help: Add missing blank line ℹ Safe fix -667 667 | def foo(): -668 668 | def bar(): -669 669 | pass - 670 |+ -670 671 | def baz(): pass +666 666 | class C: +667 667 | pass +668 668 | x = 2 + 669 |+ +669 670 | def b(): +670 671 | pass 671 672 | # end -672 673 | -E30.py:677:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:678:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -675 | def foo(): -676 | def bar(): pass -677 | def baz(): +676 | def bar(): +677 | pass +678 | def baz(): pass | ^^^ E306 -678 | pass 679 | # end | = help: Add missing blank line ℹ Safe fix -674 674 | # E306:3:5 675 675 | def foo(): -676 676 | def bar(): pass - 677 |+ -677 678 | def baz(): -678 679 | pass +676 676 | def bar(): +677 677 | pass + 678 |+ +678 679 | def baz(): pass 679 680 | # end +680 681 | E30.py:685:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -683 | def a(): -684 | x = 2 -685 | @decorator - | ^ E306 -686 | def b(): -687 | pass +683 | def foo(): +684 | def bar(): pass +685 | def baz(): + | ^^^ E306 +686 | pass +687 | # end | = help: Add missing blank line ℹ Safe fix -682 682 | # E306 -683 683 | def a(): -684 684 | x = 2 +682 682 | # E306:3:5 +683 683 | def foo(): +684 684 | def bar(): pass 685 |+ -685 686 | @decorator -686 687 | def b(): -687 688 | pass +685 686 | def baz(): +686 687 | pass +687 688 | # end -E30.py:694:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:693:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -692 | def a(): -693 | x = 2 -694 | @decorator +691 | def a(): +692 | x = 2 +693 | @decorator | ^ E306 -695 | async def b(): -696 | pass +694 | def b(): +695 | pass | = help: Add missing blank line ℹ Safe fix -691 691 | # E306 -692 692 | def a(): -693 693 | x = 2 - 694 |+ -694 695 | @decorator -695 696 | async def b(): -696 697 | pass - -E30.py:703:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -701 | def a(): -702 | x = 2 +690 690 | # E306 +691 691 | def a(): +692 692 | x = 2 + 693 |+ +693 694 | @decorator +694 695 | def b(): +695 696 | pass + +E30.py:702:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +700 | def a(): +701 | x = 2 +702 | @decorator + | ^ E306 703 | async def b(): - | ^^^^^ E306 704 | pass -705 | # end | = help: Add missing blank line ℹ Safe fix -700 700 | # E306 -701 701 | def a(): -702 702 | x = 2 - 703 |+ +699 699 | # E306 +700 700 | def a(): +701 701 | x = 2 + 702 |+ +702 703 | @decorator 703 704 | async def b(): 704 705 | pass -705 706 | # end + +E30.py:711:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +709 | def a(): +710 | x = 2 +711 | async def b(): + | ^^^^^ E306 +712 | pass +713 | # end + | + = help: Add missing blank line + +ℹ Safe fix +708 708 | # E306 +709 709 | def a(): +710 710 | x = 2 + 711 |+ +711 712 | async def b(): +712 713 | pass +713 714 | # end From 3aac64df85b0eb85c64642ddbde5c586f9860be7 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 29 Dec 2023 13:28:35 +0900 Subject: [PATCH 064/122] Add test fixture. --- .../test/fixtures/pycodestyle/E30.py | 13 + ...ules__pycodestyle__tests__E301_E30.py.snap | 52 +-- ...ules__pycodestyle__tests__E302_E30.py.snap | 270 +++++++-------- ...ules__pycodestyle__tests__E303_E30.py.snap | 196 +++++------ ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 324 +++++++++--------- 7 files changed, 510 insertions(+), 497 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 88040a65a2713..0176db51a7426 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -380,6 +380,19 @@ async def function1(): # end +# no error +if ( + cond1 + + + + + and cond2 +): + pass +#end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index c7cd64c2457a3..b3f278d455c16 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:388:5: E301 [*] Expected 1 blank line, found 0 +E30.py:401:5: E301 [*] Expected 1 blank line, found 0 | -386 | def func1(): -387 | pass -388 | def func2(): +399 | def func1(): +400 | pass +401 | def func2(): | ^^^ E301 -389 | pass -390 | # end +402 | pass +403 | # end | = help: Add missing blank line(s) ℹ Safe fix -385 385 | -386 386 | def func1(): -387 387 | pass - 388 |+ -388 389 | def func2(): -389 390 | pass -390 391 | # end +398 398 | +399 399 | def func1(): +400 400 | pass + 401 |+ +401 402 | def func2(): +402 403 | pass +403 404 | # end -E30.py:399:5: E301 [*] Expected 1 blank line, found 0 +E30.py:412:5: E301 [*] Expected 1 blank line, found 0 | -397 | pass -398 | # comment -399 | def fn2(): +410 | pass +411 | # comment +412 | def fn2(): | ^^^ E301 -400 | pass -401 | # end +413 | pass +414 | # end | = help: Add missing blank line(s) ℹ Safe fix -395 395 | -396 396 | def fn1(): -397 397 | pass - 398 |+ -398 399 | # comment -399 400 | def fn2(): -400 401 | pass +408 408 | +409 409 | def fn1(): +410 410 | pass + 411 |+ +411 412 | # comment +412 413 | def fn2(): +413 414 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 341ee4e9a2239..3da181d09149d 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,186 +1,186 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:406:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:419:1: E302 [*] Expected 2 blank lines, found 0 | -404 | # E302 -405 | """Main module.""" -406 | def fn(): +417 | # E302 +418 | """Main module.""" +419 | def fn(): | ^^^ E302 -407 | pass -408 | # end +420 | pass +421 | # end | = help: Add missing blank line(s) ℹ Safe fix -403 403 | -404 404 | # E302 -405 405 | """Main module.""" - 406 |+ - 407 |+ -406 408 | def fn(): -407 409 | pass -408 410 | # end - -E30.py:413:1: E302 [*] Expected 2 blank lines, found 0 - | -411 | # E302 -412 | import sys -413 | def get_sys_path(): +416 416 | +417 417 | # E302 +418 418 | """Main module.""" + 419 |+ + 420 |+ +419 421 | def fn(): +420 422 | pass +421 423 | # end + +E30.py:426:1: E302 [*] Expected 2 blank lines, found 0 + | +424 | # E302 +425 | import sys +426 | def get_sys_path(): | ^^^ E302 -414 | return sys.path -415 | # end +427 | return sys.path +428 | # end | = help: Add missing blank line(s) ℹ Safe fix -410 410 | -411 411 | # E302 -412 412 | import sys - 413 |+ - 414 |+ -413 415 | def get_sys_path(): -414 416 | return sys.path -415 417 | # end - -E30.py:422:1: E302 [*] Expected 2 blank lines, found 1 - | -420 | pass -421 | -422 | def b(): +423 423 | +424 424 | # E302 +425 425 | import sys + 426 |+ + 427 |+ +426 428 | def get_sys_path(): +427 429 | return sys.path +428 430 | # end + +E30.py:435:1: E302 [*] Expected 2 blank lines, found 1 + | +433 | pass +434 | +435 | def b(): | ^^^ E302 -423 | pass -424 | # end +436 | pass +437 | # end | = help: Add missing blank line(s) ℹ Safe fix -419 419 | def a(): -420 420 | pass -421 421 | - 422 |+ -422 423 | def b(): -423 424 | pass -424 425 | # end - -E30.py:433:1: E302 [*] Expected 2 blank lines, found 1 - | -431 | # comment -432 | -433 | def b(): +432 432 | def a(): +433 433 | pass +434 434 | + 435 |+ +435 436 | def b(): +436 437 | pass +437 438 | # end + +E30.py:446:1: E302 [*] Expected 2 blank lines, found 1 + | +444 | # comment +445 | +446 | def b(): | ^^^ E302 -434 | pass -435 | # end +447 | pass +448 | # end | = help: Add missing blank line(s) ℹ Safe fix -428 428 | def a(): -429 429 | pass -430 430 | - 431 |+ -431 432 | # comment -432 433 | -433 434 | def b(): - -E30.py:442:1: E302 [*] Expected 2 blank lines, found 1 - | -440 | pass -441 | -442 | async def b(): +441 441 | def a(): +442 442 | pass +443 443 | + 444 |+ +444 445 | # comment +445 446 | +446 447 | def b(): + +E30.py:455:1: E302 [*] Expected 2 blank lines, found 1 + | +453 | pass +454 | +455 | async def b(): | ^^^^^ E302 -443 | pass -444 | # end +456 | pass +457 | # end | = help: Add missing blank line(s) ℹ Safe fix -439 439 | def a(): -440 440 | pass -441 441 | - 442 |+ -442 443 | async def b(): -443 444 | pass -444 445 | # end - -E30.py:451:1: E302 [*] Expected 2 blank lines, found 1 - | -449 | pass -450 | -451 | async def x(y: int = 1): +452 452 | def a(): +453 453 | pass +454 454 | + 455 |+ +455 456 | async def b(): +456 457 | pass +457 458 | # end + +E30.py:464:1: E302 [*] Expected 2 blank lines, found 1 + | +462 | pass +463 | +464 | async def x(y: int = 1): | ^^^^^ E302 -452 | pass -453 | # end +465 | pass +466 | # end | = help: Add missing blank line(s) ℹ Safe fix -448 448 | async def x(): -449 449 | pass -450 450 | - 451 |+ -451 452 | async def x(y: int = 1): -452 453 | pass -453 454 | # end - -E30.py:459:1: E302 [*] Expected 2 blank lines, found 0 - | -457 | def bar(): -458 | pass -459 | def baz(): pass +461 461 | async def x(): +462 462 | pass +463 463 | + 464 |+ +464 465 | async def x(y: int = 1): +465 466 | pass +466 467 | # end + +E30.py:472:1: E302 [*] Expected 2 blank lines, found 0 + | +470 | def bar(): +471 | pass +472 | def baz(): pass | ^^^ E302 -460 | # end +473 | # end | = help: Add missing blank line(s) ℹ Safe fix -456 456 | # E302 -457 457 | def bar(): -458 458 | pass - 459 |+ - 460 |+ -459 461 | def baz(): pass -460 462 | # end -461 463 | - -E30.py:465:1: E302 [*] Expected 2 blank lines, found 0 - | -463 | # E302 -464 | def bar(): pass -465 | def baz(): +469 469 | # E302 +470 470 | def bar(): +471 471 | pass + 472 |+ + 473 |+ +472 474 | def baz(): pass +473 475 | # end +474 476 | + +E30.py:478:1: E302 [*] Expected 2 blank lines, found 0 + | +476 | # E302 +477 | def bar(): pass +478 | def baz(): | ^^^ E302 -466 | pass -467 | # end +479 | pass +480 | # end | = help: Add missing blank line(s) ℹ Safe fix -462 462 | -463 463 | # E302 -464 464 | def bar(): pass - 465 |+ - 466 |+ -465 467 | def baz(): -466 468 | pass -467 469 | # end - -E30.py:475:1: E302 [*] Expected 2 blank lines, found 1 - | -474 | # comment -475 | @decorator +475 475 | +476 476 | # E302 +477 477 | def bar(): pass + 478 |+ + 479 |+ +478 480 | def baz(): +479 481 | pass +480 482 | # end + +E30.py:488:1: E302 [*] Expected 2 blank lines, found 1 + | +487 | # comment +488 | @decorator | ^ E302 -476 | def g(): -477 | pass +489 | def g(): +490 | pass | = help: Add missing blank line(s) ℹ Safe fix -471 471 | def f(): -472 472 | pass -473 473 | - 474 |+ -474 475 | # comment -475 476 | @decorator -476 477 | def g(): +484 484 | def f(): +485 485 | pass +486 486 | + 487 |+ +487 488 | # comment +488 489 | @decorator +489 490 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 0be66fb4d63d7..b25448054562b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:486:5: E303 [*] Too many blank lines (2) +E30.py:499:5: E303 [*] Too many blank lines (2) | -486 | # arbitrary comment +499 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -487 | -488 | def inner(): # E306 not expected +500 | +501 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -482 482 | def fn(): -483 483 | _ = None -484 484 | -485 |- -486 485 | # arbitrary comment -487 486 | -488 487 | def inner(): # E306 not expected +495 495 | def fn(): +496 496 | _ = None +497 497 | +498 |- +499 498 | # arbitrary comment +500 499 | +501 500 | def inner(): # E306 not expected -E30.py:498:5: E303 [*] Too many blank lines (2) +E30.py:511:5: E303 [*] Too many blank lines (2) | -498 | # arbitrary comment +511 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -499 | def inner(): # E306 not expected -500 | pass +512 | def inner(): # E306 not expected +513 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -494 494 | def fn(): -495 495 | _ = None -496 496 | -497 |- -498 497 | # arbitrary comment -499 498 | def inner(): # E306 not expected -500 499 | pass +507 507 | def fn(): +508 508 | _ = None +509 509 | +510 |- +511 510 | # arbitrary comment +512 511 | def inner(): # E306 not expected +513 512 | pass -E30.py:509:1: E303 [*] Too many blank lines (3) +E30.py:522:1: E303 [*] Too many blank lines (3) | -509 | print() +522 | print() | ^^^^^ E303 -510 | # end +523 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -505 505 | print() -506 506 | -507 507 | -508 |- -509 508 | print() -510 509 | # end -511 510 | +518 518 | print() +519 519 | +520 520 | +521 |- +522 521 | print() +523 522 | # end +524 523 | -E30.py:518:1: E303 [*] Too many blank lines (3) +E30.py:531:1: E303 [*] Too many blank lines (3) | -518 | # comment +531 | # comment | ^^^^^^^^^ E303 -519 | -520 | print() +532 | +533 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -514 514 | print() -515 515 | -516 516 | -517 |- -518 517 | # comment -519 518 | -520 519 | print() +527 527 | print() +528 528 | +529 529 | +530 |- +531 530 | # comment +532 531 | +533 532 | print() -E30.py:529:5: E303 [*] Too many blank lines (2) +E30.py:542:5: E303 [*] Too many blank lines (2) | -529 | # comment +542 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -525 525 | def a(): -526 526 | print() -527 527 | -528 |- -529 528 | # comment -530 529 | -531 530 | +538 538 | def a(): +539 539 | print() +540 540 | +541 |- +542 541 | # comment +543 542 | +544 543 | -E30.py:532:5: E303 [*] Too many blank lines (2) +E30.py:545:5: E303 [*] Too many blank lines (2) | -532 | # another comment +545 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -533 | -534 | print() +546 | +547 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -528 528 | -529 529 | # comment -530 530 | -531 |- -532 531 | # another comment -533 532 | -534 533 | print() - -E30.py:543:1: E303 [*] Too many blank lines (3) - | -543 | / """This class docstring comes on line 5. -544 | | It gives error E303: too many blank lines (3) -545 | | """ +541 541 | +542 542 | # comment +543 543 | +544 |- +545 544 | # another comment +546 545 | +547 546 | print() + +E30.py:556:1: E303 [*] Too many blank lines (3) + | +556 | / """This class docstring comes on line 5. +557 | | It gives error E303: too many blank lines (3) +558 | | """ | |___^ E303 -546 | # end +559 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -539 539 | #!python -540 540 | -541 541 | -542 |- -543 542 | """This class docstring comes on line 5. -544 543 | It gives error E303: too many blank lines (3) -545 544 | """ +552 552 | #!python +553 553 | +554 554 | +555 |- +556 555 | """This class docstring comes on line 5. +557 556 | It gives error E303: too many blank lines (3) +558 557 | """ -E30.py:555:5: E303 [*] Too many blank lines (2) +E30.py:568:5: E303 [*] Too many blank lines (2) | -555 | def b(self): +568 | def b(self): | ^^^ E303 -556 | pass -557 | # end +569 | pass +570 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -551 551 | def a(self): -552 552 | pass -553 553 | -554 |- -555 554 | def b(self): -556 555 | pass -557 556 | # end +564 564 | def a(self): +565 565 | pass +566 566 | +567 |- +568 567 | def b(self): +569 568 | pass +570 569 | # end -E30.py:565:5: E303 [*] Too many blank lines (2) +E30.py:578:5: E303 [*] Too many blank lines (2) | -565 | a = 2 +578 | a = 2 | ^ E303 -566 | # end +579 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -561 561 | if True: -562 562 | a = 1 -563 563 | -564 |- -565 564 | a = 2 -566 565 | # end -567 566 | +574 574 | if True: +575 575 | a = 1 +576 576 | +577 |- +578 577 | a = 2 +579 578 | # end +580 579 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 91280c8f8896f..fd75e6120a76f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:572:1: E304 [*] blank lines found after function decorator +E30.py:585:1: E304 [*] blank lines found after function decorator | -570 | @decorator -571 | -572 | def function(): +583 | @decorator +584 | +585 | def function(): | ^^^ E304 -573 | pass -574 | # end +586 | pass +587 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -568 568 | -569 569 | # E304 -570 570 | @decorator -571 |- -572 571 | def function(): -573 572 | pass -574 573 | # end +581 581 | +582 582 | # E304 +583 583 | @decorator +584 |- +585 584 | def function(): +586 585 | pass +587 586 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 74b881ba5dc45..942fdb202bff6 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:584:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:597:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -583 | # another comment -584 | fn() +596 | # another comment +597 | fn() | ^^ E305 -585 | # end +598 | # end | = help: Add missing blank line(s) ℹ Safe fix -581 581 | # comment -582 582 | -583 583 | # another comment - 584 |+ - 585 |+ -584 586 | fn() -585 587 | # end -586 588 | +594 594 | # comment +595 595 | +596 596 | # another comment + 597 |+ + 598 |+ +597 599 | fn() +598 600 | # end +599 601 | -E30.py:595:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:608:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -594 | # another comment -595 | a = 1 +607 | # another comment +608 | a = 1 | ^ E305 -596 | # end +609 | # end | = help: Add missing blank line(s) ℹ Safe fix -592 592 | # comment -593 593 | -594 594 | # another comment - 595 |+ - 596 |+ -595 597 | a = 1 -596 598 | # end -597 599 | +605 605 | # comment +606 606 | +607 607 | # another comment + 608 |+ + 609 |+ +608 610 | a = 1 +609 611 | # end +610 612 | -E30.py:607:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:620:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -605 | # another comment -606 | -607 | try: +618 | # another comment +619 | +620 | try: | ^^^ E305 -608 | fn() -609 | except Exception: +621 | fn() +622 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -604 604 | -605 605 | # another comment -606 606 | - 607 |+ -607 608 | try: -608 609 | fn() -609 610 | except Exception: +617 617 | +618 618 | # another comment +619 619 | + 620 |+ +620 621 | try: +621 622 | fn() +622 623 | except Exception: -E30.py:619:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:632:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -618 | # Two spaces before comments, too. -619 | if a(): +631 | # Two spaces before comments, too. +632 | if a(): | ^^ E305 -620 | a() -621 | # end +633 | a() +634 | # end | = help: Add missing blank line(s) ℹ Safe fix -616 616 | print -617 617 | -618 618 | # Two spaces before comments, too. - 619 |+ - 620 |+ -619 621 | if a(): -620 622 | a() -621 623 | # end +629 629 | print +630 630 | +631 631 | # Two spaces before comments, too. + 632 |+ + 633 |+ +632 634 | if a(): +633 635 | a() +634 636 | # end -E30.py:632:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:645:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -630 | blah, blah -631 | -632 | if __name__ == '__main__': +643 | blah, blah +644 | +645 | if __name__ == '__main__': | ^^ E305 -633 | main() -634 | # end +646 | main() +647 | # end | = help: Add missing blank line(s) ℹ Safe fix -629 629 | def main(): -630 630 | blah, blah -631 631 | - 632 |+ -632 633 | if __name__ == '__main__': -633 634 | main() -634 635 | # end +642 642 | def main(): +643 643 | blah, blah +644 644 | + 645 |+ +645 646 | if __name__ == '__main__': +646 647 | main() +647 648 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 7955b12f2947f..44b512584a493 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,223 +1,223 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:640:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:653:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -638 | def a(): -639 | x = 1 -640 | def b(): +651 | def a(): +652 | x = 1 +653 | def b(): | ^^^ E306 -641 | pass -642 | # end +654 | pass +655 | # end | = help: Add missing blank line ℹ Safe fix -637 637 | # E306:3:5 -638 638 | def a(): -639 639 | x = 1 - 640 |+ -640 641 | def b(): -641 642 | pass -642 643 | # end - -E30.py:648:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -646 | async def a(): -647 | x = 1 -648 | def b(): +650 650 | # E306:3:5 +651 651 | def a(): +652 652 | x = 1 + 653 |+ +653 654 | def b(): +654 655 | pass +655 656 | # end + +E30.py:661:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +659 | async def a(): +660 | x = 1 +661 | def b(): | ^^^ E306 -649 | pass -650 | # end +662 | pass +663 | # end | = help: Add missing blank line ℹ Safe fix -645 645 | #: E306:3:5 -646 646 | async def a(): -647 647 | x = 1 - 648 |+ -648 649 | def b(): -649 650 | pass -650 651 | # end - -E30.py:656:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -654 | def a(): -655 | x = 2 -656 | def b(): +658 658 | #: E306:3:5 +659 659 | async def a(): +660 660 | x = 1 + 661 |+ +661 662 | def b(): +662 663 | pass +663 664 | # end + +E30.py:669:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +667 | def a(): +668 | x = 2 +669 | def b(): | ^^^ E306 -657 | x = 1 -658 | def c(): +670 | x = 1 +671 | def c(): | = help: Add missing blank line ℹ Safe fix -653 653 | #: E306:3:5 E306:5:9 -654 654 | def a(): -655 655 | x = 2 - 656 |+ -656 657 | def b(): -657 658 | x = 1 -658 659 | def c(): - -E30.py:658:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -656 | def b(): -657 | x = 1 -658 | def c(): +666 666 | #: E306:3:5 E306:5:9 +667 667 | def a(): +668 668 | x = 2 + 669 |+ +669 670 | def b(): +670 671 | x = 1 +671 672 | def c(): + +E30.py:671:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +669 | def b(): +670 | x = 1 +671 | def c(): | ^^^ E306 -659 | pass -660 | # end +672 | pass +673 | # end | = help: Add missing blank line ℹ Safe fix -655 655 | x = 2 -656 656 | def b(): -657 657 | x = 1 - 658 |+ -658 659 | def c(): -659 660 | pass -660 661 | # end - -E30.py:666:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -664 | def a(): -665 | x = 1 -666 | class C: +668 668 | x = 2 +669 669 | def b(): +670 670 | x = 1 + 671 |+ +671 672 | def c(): +672 673 | pass +673 674 | # end + +E30.py:679:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +677 | def a(): +678 | x = 1 +679 | class C: | ^^^^^ E306 -667 | pass -668 | x = 2 +680 | pass +681 | x = 2 | = help: Add missing blank line ℹ Safe fix -663 663 | # E306:3:5 E306:6:5 -664 664 | def a(): -665 665 | x = 1 - 666 |+ -666 667 | class C: -667 668 | pass -668 669 | x = 2 - -E30.py:669:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -667 | pass -668 | x = 2 -669 | def b(): +676 676 | # E306:3:5 E306:6:5 +677 677 | def a(): +678 678 | x = 1 + 679 |+ +679 680 | class C: +680 681 | pass +681 682 | x = 2 + +E30.py:682:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +680 | pass +681 | x = 2 +682 | def b(): | ^^^ E306 -670 | pass -671 | # end +683 | pass +684 | # end | = help: Add missing blank line ℹ Safe fix -666 666 | class C: -667 667 | pass -668 668 | x = 2 - 669 |+ -669 670 | def b(): -670 671 | pass -671 672 | # end - -E30.py:678:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -676 | def bar(): -677 | pass -678 | def baz(): pass +679 679 | class C: +680 680 | pass +681 681 | x = 2 + 682 |+ +682 683 | def b(): +683 684 | pass +684 685 | # end + +E30.py:691:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +689 | def bar(): +690 | pass +691 | def baz(): pass | ^^^ E306 -679 | # end +692 | # end | = help: Add missing blank line ℹ Safe fix -675 675 | def foo(): -676 676 | def bar(): -677 677 | pass - 678 |+ -678 679 | def baz(): pass -679 680 | # end -680 681 | - -E30.py:685:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -683 | def foo(): -684 | def bar(): pass -685 | def baz(): +688 688 | def foo(): +689 689 | def bar(): +690 690 | pass + 691 |+ +691 692 | def baz(): pass +692 693 | # end +693 694 | + +E30.py:698:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +696 | def foo(): +697 | def bar(): pass +698 | def baz(): | ^^^ E306 -686 | pass -687 | # end +699 | pass +700 | # end | = help: Add missing blank line ℹ Safe fix -682 682 | # E306:3:5 -683 683 | def foo(): -684 684 | def bar(): pass - 685 |+ -685 686 | def baz(): -686 687 | pass -687 688 | # end - -E30.py:693:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -691 | def a(): -692 | x = 2 -693 | @decorator +695 695 | # E306:3:5 +696 696 | def foo(): +697 697 | def bar(): pass + 698 |+ +698 699 | def baz(): +699 700 | pass +700 701 | # end + +E30.py:706:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +704 | def a(): +705 | x = 2 +706 | @decorator | ^ E306 -694 | def b(): -695 | pass +707 | def b(): +708 | pass | = help: Add missing blank line ℹ Safe fix -690 690 | # E306 -691 691 | def a(): -692 692 | x = 2 - 693 |+ -693 694 | @decorator -694 695 | def b(): -695 696 | pass - -E30.py:702:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -700 | def a(): -701 | x = 2 -702 | @decorator +703 703 | # E306 +704 704 | def a(): +705 705 | x = 2 + 706 |+ +706 707 | @decorator +707 708 | def b(): +708 709 | pass + +E30.py:715:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +713 | def a(): +714 | x = 2 +715 | @decorator | ^ E306 -703 | async def b(): -704 | pass +716 | async def b(): +717 | pass | = help: Add missing blank line ℹ Safe fix -699 699 | # E306 -700 700 | def a(): -701 701 | x = 2 - 702 |+ -702 703 | @decorator -703 704 | async def b(): -704 705 | pass - -E30.py:711:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -709 | def a(): -710 | x = 2 -711 | async def b(): +712 712 | # E306 +713 713 | def a(): +714 714 | x = 2 + 715 |+ +715 716 | @decorator +716 717 | async def b(): +717 718 | pass + +E30.py:724:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +722 | def a(): +723 | x = 2 +724 | async def b(): | ^^^^^ E306 -712 | pass -713 | # end +725 | pass +726 | # end | = help: Add missing blank line ℹ Safe fix -708 708 | # E306 -709 709 | def a(): -710 710 | x = 2 - 711 |+ -711 712 | async def b(): -712 713 | pass -713 714 | # end +721 721 | # E306 +722 722 | def a(): +723 723 | x = 2 + 724 |+ +724 725 | async def b(): +725 726 | pass +726 727 | # end From b7727c5e73c646637883e8e24a3802575d09a9e8 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 29 Dec 2023 17:31:45 +0900 Subject: [PATCH 065/122] Remove async as a top level token. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 11f191e45e3fe..98c99389f37bc 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -324,12 +324,12 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { /// Returns `true` if the token is Async, Class or Def fn is_top_level_token(token: &Option) -> bool { - matches!(&token, Some(Tok::Class | Tok::Def | Tok::Async)) + matches!(&token, Some(Tok::Class | Tok::Def)) } /// Returns `true` if the token is At, Async, Class or Def fn is_top_level_token_or_decorator(token: &Tok) -> bool { - matches!(&token, Tok::Class | Tok::Def | Tok::Async | Tok::At) + matches!(&token, Tok::Class | Tok::Def | Tok::At) } #[derive(Debug)] From ba7d8d7236aa87703ff76f13697cce5c4f11aef3 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 18:08:04 +0900 Subject: [PATCH 066/122] Fix is_top_level_token docstring. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 98c99389f37bc..c96a706c09207 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -322,7 +322,8 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { } } -/// Returns `true` if the token is Async, Class or Def +/// Returns `true` if the token is a top level token. +/// It is sufficient to test for Class and Def since the LinePreprocessor ignores Async tokens. fn is_top_level_token(token: &Option) -> bool { matches!(&token, Some(Tok::Class | Tok::Def)) } From 0bcdf7daeeb3a6ffb7374163775edd5527b56c61 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 18:24:13 +0900 Subject: [PATCH 067/122] Use usize for preceding_blank_characters. --- .../rules/pycodestyle/rules/blank_lines.rs | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index c96a706c09207..7e1d46dd8b2f3 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -323,7 +323,7 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { } /// Returns `true` if the token is a top level token. -/// It is sufficient to test for Class and Def since the LinePreprocessor ignores Async tokens. +/// It is sufficient to test for Class and Def since the `LinePreprocessor` ignores Async tokens. fn is_top_level_token(token: &Option) -> bool { matches!(&token, Some(Tok::Class | Tok::Def)) } @@ -344,7 +344,7 @@ struct LogicalLineInfo { indent_level: usize, blank_lines: u32, preceding_blank_lines: u32, - preceding_blank_characters: TextSize, + preceding_blank_characters: usize, } /// Iterator that processes tokens until a full logical line (or comment line) is "built". @@ -357,7 +357,7 @@ struct LinePreprocessor<'a> { /// Number of consecutive blank lines. current_blank_lines: u32, /// Number of blank characters in the blank lines (\n vs \r\n for example). - current_blank_characters: TextSize, + current_blank_characters: usize, } impl<'a> LinePreprocessor<'a> { @@ -367,7 +367,7 @@ impl<'a> LinePreprocessor<'a> { locator, previous_blank_lines: 0, current_blank_lines: 0, - current_blank_characters: 0.into(), + current_blank_characters: 0, } } } @@ -442,7 +442,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { if matches!(first_token, Some(Tok::NonLogicalNewline)) { self.current_blank_lines += 1; self.current_blank_characters += - range.end() - first_range.start() + TextSize::new(1); + range.end().to_usize() - first_range.start().to_usize() + 1; return self.next(); } @@ -467,7 +467,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { self.previous_blank_lines = 0; } self.current_blank_lines = 0; - self.current_blank_characters = 0.into(); + self.current_blank_characters = 0; return Some(logical_line); } _ => {} @@ -615,9 +615,13 @@ impl BlankLinesChecker { Diagnostic::new(TooManyBlankLines(line.blank_lines), line.first_token_range); let chars_to_remove = if line.indent_level > 0 { - line.preceding_blank_characters.to_u32() - BLANK_LINES_METHOD_LEVEL + u32::try_from(line.preceding_blank_characters) + .expect("Number of blank characters to be small.") + - BLANK_LINES_METHOD_LEVEL } else { - line.preceding_blank_characters.to_u32() - BLANK_LINES_TOP_LEVEL + u32::try_from(line.preceding_blank_characters) + .expect("Number of blank characters to be small.") + - BLANK_LINES_TOP_LEVEL }; let end = locator.line_start(line.first_token_range.start()); let start = end - TextSize::new(chars_to_remove); @@ -633,7 +637,12 @@ impl BlankLinesChecker { let range = line.first_token_range; diagnostic.set_fix(Fix::safe_edit(Edit::deletion( - locator.line_start(range.start()) - line.preceding_blank_characters, + locator.line_start(range.start()) + - TextSize::new( + line.preceding_blank_characters + .try_into() + .expect("Number of blank characters to be small."), + ), locator.line_start(range.start()), ))); From ede587a6c14a8e1d8172579aadc8cfce598d387c Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 20:28:46 +0900 Subject: [PATCH 068/122] Use TokenKind instead of Tok --- .../rules/pycodestyle/rules/blank_lines.rs | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 7e1d46dd8b2f3..4ac8e1a386707 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -2,6 +2,7 @@ use ruff_diagnostics::AlwaysFixableViolation; use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Edit; use ruff_diagnostics::Fix; +use ruff_python_parser::Tok; use std::iter::Flatten; use std::slice::Iter; @@ -9,7 +10,7 @@ use ruff_macros::{derive_message_formats, violation}; use ruff_python_codegen::Stylist; use ruff_python_parser::lexer::LexResult; use ruff_python_parser::lexer::LexicalError; -use ruff_python_parser::Tok; +use ruff_python_parser::TokenKind; use ruff_source_file::Locator; use ruff_text_size::TextRange; @@ -29,7 +30,7 @@ pub(crate) struct BlankLinesChecker { /// Used for the fix in case a comment separates two non-comment logical lines to make the comment "stick" /// to the second line instead of the first. last_non_comment_line_end: TextSize, - previous_unindented_token: Option, + previous_unindented_token: Option, } /// Number of blank lines around top level classes and functions. @@ -324,20 +325,20 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { /// Returns `true` if the token is a top level token. /// It is sufficient to test for Class and Def since the `LinePreprocessor` ignores Async tokens. -fn is_top_level_token(token: &Option) -> bool { - matches!(&token, Some(Tok::Class | Tok::Def)) +fn is_top_level_token(token: &Option) -> bool { + matches!(&token, Some(TokenKind::Class | TokenKind::Def)) } /// Returns `true` if the token is At, Async, Class or Def -fn is_top_level_token_or_decorator(token: &Tok) -> bool { - matches!(&token, Tok::Class | Tok::Def | Tok::At) +fn is_top_level_token_or_decorator(token: &TokenKind) -> bool { + matches!(&token, TokenKind::Class | TokenKind::Def | TokenKind::At) } #[derive(Debug)] struct LogicalLineInfo { - first_token: Tok, + first_token: TokenKind, first_token_range: TextRange, - last_token: Tok, + last_token: TokenKind, last_token_range: TextRange, is_comment_only: bool, is_docstring: bool, @@ -378,57 +379,58 @@ impl<'a> Iterator for LinePreprocessor<'a> { fn next(&mut self) -> Option { let mut line_is_comment_only = true; let mut is_docstring = true; - let mut first_token: Option = None; + let mut first_token: Option = None; let mut first_token_range: Option = None; - let mut last_token: Option = None; + let mut last_token: Option = None; let mut parens = 0u32; while let Some((token, range)) = self.tokens.next() { - if matches!(token, Tok::Indent | Tok::Dedent) { + let token = TokenKind::from_token(token); + if matches!(token, TokenKind::Indent | TokenKind::Dedent) { continue; } - if !matches!(token, Tok::Newline) { + if !matches!(token, TokenKind::Newline) { // Ideally, we would like to have a "async def" token since we care about the "def" part. // As a work around, we ignore the first token if it is "async". - if first_token.is_none() && !matches!(token, Tok::Async) { - first_token = Some(token.clone()); + if first_token.is_none() && !matches!(token, TokenKind::Async) { + first_token = Some(token); } if first_token_range.is_none() { first_token_range = Some(*range); } - if !matches!(token, Tok::NonLogicalNewline) { - if !matches!(token, Tok::Comment(_)) { + if !matches!(token, TokenKind::NonLogicalNewline) { + if !matches!(token, TokenKind::Comment) { line_is_comment_only = false; } // Allow a comment to follow a docstring. - if !matches!(token, Tok::String { .. } | Tok::Comment(_)) { + if !matches!(token, TokenKind::String { .. } | TokenKind::Comment) { is_docstring = false; } } - last_token = Some(token.clone()); + last_token = Some(token); } match token { - Tok::Lbrace | Tok::Lpar | Tok::Lsqb => { + TokenKind::Lbrace | TokenKind::Lpar | TokenKind::Lsqb => { parens = parens.saturating_add(1); } - Tok::Rbrace | Tok::Rpar | Tok::Rsqb => { + TokenKind::Rbrace | TokenKind::Rpar | TokenKind::Rsqb => { parens = parens.saturating_sub(1); } - Tok::Newline | Tok::NonLogicalNewline if parens == 0 => { + TokenKind::Newline | TokenKind::NonLogicalNewline if parens == 0 => { let last_token_range = *range; - if !matches!(first_token, Some(Tok::String { .. })) { + if !matches!(first_token, Some(TokenKind::String { .. })) { is_docstring = false; } let first_range = first_token_range.unwrap(); - let range = if matches!(first_token, Some(Tok::Indent)) { + let range = if matches!(first_token, Some(TokenKind::Indent)) { first_range } else { TextRange::new( @@ -439,7 +441,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { let indent_level = expand_indent(self.locator.slice(range)); // Empty line - if matches!(first_token, Some(Tok::NonLogicalNewline)) { + if matches!(first_token, Some(TokenKind::NonLogicalNewline)) { self.current_blank_lines += 1; self.current_blank_characters += range.end().to_usize() - first_range.start().to_usize() + 1; @@ -451,7 +453,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { } let logical_line = LogicalLineInfo { - first_token: first_token.clone().unwrap(), + first_token: first_token.unwrap(), first_token_range: first_range, last_token: last_token.unwrap(), last_token_range, @@ -558,7 +560,7 @@ impl BlankLinesChecker { if self.is_not_first_logical_line { if line.preceding_blank_lines == 0 // Only applies to methods. - && line.first_token == Tok::Def + && line.first_token == TokenKind::Def && matches!(self.class_status, Status::Inside(_)) // The class/parent method's docstring can directly precede the def. && !matches!(self.follows, Follows::Docstring) @@ -584,7 +586,7 @@ impl BlankLinesChecker { // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !matches!(self.follows, Follows::Decorator) // Allow groups of one-liners. - && !(matches!(self.follows, Follows::Def) && !matches!(line.last_token, Tok::Colon)) + && !(matches!(self.follows, Follows::Def) && !matches!(line.last_token, TokenKind::Colon)) // Only trigger on non-indented classes and functions (for example functions within an if are ignored) && line.indent_level == 0 // Only apply to functions or classes. @@ -684,7 +686,7 @@ impl BlankLinesChecker { // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= line.indent_level) // Allow groups of one-liners. - && !(matches!(self.follows, Follows::Def) && !matches!(line.last_token, Tok::Colon)) + && !(matches!(self.follows, Follows::Def) && !matches!(line.last_token, TokenKind::Colon)) { // E306 let mut diagnostic = Diagnostic::new( @@ -702,22 +704,22 @@ impl BlankLinesChecker { } match line.first_token { - Tok::Class => { + TokenKind::Class => { if matches!(self.class_status, Status::Outside) { self.class_status = Status::Inside(line.indent_level + indent_size); } self.follows = Follows::Other; } - Tok::At => { + TokenKind::At => { self.follows = Follows::Decorator; } - Tok::Def | Tok::Async => { + TokenKind::Def | TokenKind::Async => { if matches!(self.fn_status, Status::Outside) { self.fn_status = Status::Inside(line.indent_level + indent_size); } self.follows = Follows::Def; } - Tok::Comment(_) => {} + TokenKind::Comment => {} _ => { self.follows = Follows::Other; } @@ -734,8 +736,8 @@ impl BlankLinesChecker { self.last_non_comment_line_end = line.last_token_range.end(); - if line.indent_level == 0 && !matches!(line.first_token, Tok::Comment(_)) { - self.previous_unindented_token = Some(line.first_token.clone()); + if line.indent_level == 0 && !matches!(line.first_token, TokenKind::Comment) { + self.previous_unindented_token = Some(line.first_token); } } } From f3305841bc070904a6a157caa08477fd5a16eda0 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 20:47:26 +0900 Subject: [PATCH 069/122] Use == comparison instead of matches! where possible --- .../src/rules/pycodestyle/rules/blank_lines.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 4ac8e1a386707..f7f3f3c73552b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -390,18 +390,18 @@ impl<'a> Iterator for LinePreprocessor<'a> { continue; } - if !matches!(token, TokenKind::Newline) { + if token != TokenKind::Newline { // Ideally, we would like to have a "async def" token since we care about the "def" part. // As a work around, we ignore the first token if it is "async". - if first_token.is_none() && !matches!(token, TokenKind::Async) { + if first_token.is_none() && token != TokenKind::Async { first_token = Some(token); } if first_token_range.is_none() { first_token_range = Some(*range); } - if !matches!(token, TokenKind::NonLogicalNewline) { - if !matches!(token, TokenKind::Comment) { + if token != TokenKind::NonLogicalNewline { + if token != TokenKind::Comment { line_is_comment_only = false; } @@ -424,13 +424,13 @@ impl<'a> Iterator for LinePreprocessor<'a> { TokenKind::Newline | TokenKind::NonLogicalNewline if parens == 0 => { let last_token_range = *range; - if !matches!(first_token, Some(TokenKind::String { .. })) { + if first_token != Some(TokenKind::String) { is_docstring = false; } let first_range = first_token_range.unwrap(); - let range = if matches!(first_token, Some(TokenKind::Indent)) { + let range = if first_token == Some(TokenKind::Indent) { first_range } else { TextRange::new( @@ -441,7 +441,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { let indent_level = expand_indent(self.locator.slice(range)); // Empty line - if matches!(first_token, Some(TokenKind::NonLogicalNewline)) { + if first_token == Some(TokenKind::NonLogicalNewline) { self.current_blank_lines += 1; self.current_blank_characters += range.end().to_usize() - first_range.start().to_usize() + 1; @@ -686,7 +686,7 @@ impl BlankLinesChecker { // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= line.indent_level) // Allow groups of one-liners. - && !(matches!(self.follows, Follows::Def) && !matches!(line.last_token, TokenKind::Colon)) + && !(matches!(self.follows, Follows::Def) && line.last_token != TokenKind::Colon) { // E306 let mut diagnostic = Diagnostic::new( @@ -736,7 +736,7 @@ impl BlankLinesChecker { self.last_non_comment_line_end = line.last_token_range.end(); - if line.indent_level == 0 && !matches!(line.first_token, TokenKind::Comment) { + if line.indent_level == 0 && line.first_token != TokenKind::Comment { self.previous_unindented_token = Some(line.first_token); } } From 4437cc97bb76dec113075c5633546daef932fa91 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 20:57:37 +0900 Subject: [PATCH 070/122] Use TokenKind::is_trivia to reduce nesting. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index f7f3f3c73552b..090accc7c1121 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -400,15 +400,16 @@ impl<'a> Iterator for LinePreprocessor<'a> { first_token_range = Some(*range); } - if token != TokenKind::NonLogicalNewline { - if token != TokenKind::Comment { - line_is_comment_only = false; - } + if !token.is_trivia() { + line_is_comment_only = false; + } - // Allow a comment to follow a docstring. - if !matches!(token, TokenKind::String { .. } | TokenKind::Comment) { - is_docstring = false; - } + // Allow a comment to follow a docstring. + if !matches!( + token, + TokenKind::String { .. } | TokenKind::NonLogicalNewline | TokenKind::Comment + ) { + is_docstring = false; } last_token = Some(token); From 3647ec550e73a56ce0df5805da726b740d5d7471 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 21:03:01 +0900 Subject: [PATCH 071/122] Remove dead if branch. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 090accc7c1121..67fbf11101be8 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -431,14 +431,11 @@ impl<'a> Iterator for LinePreprocessor<'a> { let first_range = first_token_range.unwrap(); - let range = if first_token == Some(TokenKind::Indent) { - first_range - } else { - TextRange::new( - self.locator.line_start(first_range.start()), - first_range.start(), - ) - }; + let range = TextRange::new( + self.locator.line_start(first_range.start()), + first_range.start(), + ); + let indent_level = expand_indent(self.locator.slice(range)); // Empty line From 26417b91d24646d34945e9d910462311c7c00100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ho=C3=ABl=20Bagard?= <34478245+hoel-bagard@users.noreply.github.com> Date: Wed, 10 Jan 2024 21:23:35 +0900 Subject: [PATCH 072/122] Update crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs Co-authored-by: Micha Reiser --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 67fbf11101be8..a79d6889e920e 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -87,7 +87,9 @@ enum Status { /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E301.html) #[violation] -pub struct BlankLineBetweenMethods(pub u32); +pub struct BlankLineBetweenMethods { + actual_blank_lines: u32 +} impl AlwaysFixableViolation for BlankLineBetweenMethods { #[derive_message_formats] From 8bc8960e7bfe5c7dfa840cbf733474eb3f118b2e Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 21:35:10 +0900 Subject: [PATCH 073/122] Give violations' u32 field a name and make it private. --- .../rules/pycodestyle/rules/blank_lines.rs | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index a79d6889e920e..7c7b6cc713556 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -88,13 +88,15 @@ enum Status { /// - [Flake 8 rule](https://www.flake8rules.com/rules/E301.html) #[violation] pub struct BlankLineBetweenMethods { - actual_blank_lines: u32 + actual_blank_lines: u32, } impl AlwaysFixableViolation for BlankLineBetweenMethods { #[derive_message_formats] fn message(&self) -> String { - let BlankLineBetweenMethods(nb_blank_lines) = self; + let BlankLineBetweenMethods { + actual_blank_lines: nb_blank_lines, + } = self; format!("Expected {BLANK_LINES_METHOD_LEVEL:?} blank line, found {nb_blank_lines}") } @@ -133,12 +135,17 @@ impl AlwaysFixableViolation for BlankLineBetweenMethods { /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E302.html) #[violation] -pub struct BlankLinesTopLevel(pub u32); +pub struct BlankLinesTopLevel { + actual_blank_lines: u32, +} impl AlwaysFixableViolation for BlankLinesTopLevel { #[derive_message_formats] fn message(&self) -> String { - let BlankLinesTopLevel(nb_blank_lines) = self; + let BlankLinesTopLevel { + actual_blank_lines: nb_blank_lines, + } = self; + format!("Expected {BLANK_LINES_TOP_LEVEL:?} blank lines, found {nb_blank_lines}") } @@ -180,12 +187,16 @@ impl AlwaysFixableViolation for BlankLinesTopLevel { /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E303.html) #[violation] -pub struct TooManyBlankLines(pub u32); +pub struct TooManyBlankLines { + actual_blank_lines: u32, +} impl AlwaysFixableViolation for TooManyBlankLines { #[derive_message_formats] fn message(&self) -> String { - let TooManyBlankLines(nb_blank_lines) = self; + let TooManyBlankLines { + actual_blank_lines: nb_blank_lines, + } = self; format!("Too many blank lines ({nb_blank_lines})") } @@ -266,12 +277,16 @@ impl AlwaysFixableViolation for BlankLineAfterDecorator { /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E305.html) #[violation] -pub struct BlankLinesAfterFunctionOrClass(pub u32); +pub struct BlankLinesAfterFunctionOrClass { + actual_blank_lines: u32, +} impl AlwaysFixableViolation for BlankLinesAfterFunctionOrClass { #[derive_message_formats] fn message(&self) -> String { - let BlankLinesAfterFunctionOrClass(blank_lines) = self; + let BlankLinesAfterFunctionOrClass { + actual_blank_lines: blank_lines, + } = self; format!("expected 2 blank lines after class or function definition, found ({blank_lines})") } @@ -311,12 +326,16 @@ impl AlwaysFixableViolation for BlankLinesAfterFunctionOrClass { /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E306.html) #[violation] -pub struct BlankLinesBeforeNestedDefinition(pub u32); +pub struct BlankLinesBeforeNestedDefinition { + actual_blank_lines: u32, +} impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { #[derive_message_formats] fn message(&self) -> String { - let BlankLinesBeforeNestedDefinition(blank_lines) = self; + let BlankLinesBeforeNestedDefinition { + actual_blank_lines: blank_lines, + } = self; format!("Expected 1 blank line before a nested definition, found {blank_lines}") } @@ -571,7 +590,9 @@ impl BlankLinesChecker { { // E301 let mut diagnostic = Diagnostic::new( - BlankLineBetweenMethods(line.preceding_blank_lines), + BlankLineBetweenMethods { + actual_blank_lines: line.preceding_blank_lines, + }, line.first_token_range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( @@ -594,7 +615,9 @@ impl BlankLinesChecker { { // E302 let mut diagnostic = Diagnostic::new( - BlankLinesTopLevel(line.preceding_blank_lines), + BlankLinesTopLevel { + actual_blank_lines: line.preceding_blank_lines, + }, line.first_token_range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( @@ -613,8 +636,12 @@ impl BlankLinesChecker { || (line.indent_level > 0 && line.blank_lines > BLANK_LINES_METHOD_LEVEL) { // E303 - let mut diagnostic = - Diagnostic::new(TooManyBlankLines(line.blank_lines), line.first_token_range); + let mut diagnostic = Diagnostic::new( + TooManyBlankLines { + actual_blank_lines: line.blank_lines, + }, + line.first_token_range, + ); let chars_to_remove = if line.indent_level > 0 { u32::try_from(line.preceding_blank_characters) @@ -659,7 +686,9 @@ impl BlankLinesChecker { { // E305 let mut diagnostic = Diagnostic::new( - BlankLinesAfterFunctionOrClass(line.blank_lines), + BlankLinesAfterFunctionOrClass { + actual_blank_lines: line.blank_lines, + }, line.first_token_range, ); @@ -690,7 +719,9 @@ impl BlankLinesChecker { { // E306 let mut diagnostic = Diagnostic::new( - BlankLinesBeforeNestedDefinition(line.blank_lines), + BlankLinesBeforeNestedDefinition { + actual_blank_lines: line.blank_lines, + }, line.first_token_range, ); From 22cd072dfaf1af21255d703bd2f0a5acd12d82d0 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 21:51:15 +0900 Subject: [PATCH 074/122] Simplify and explain is_docstring flag detection. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 7c7b6cc713556..58e49e78518b4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -425,11 +425,9 @@ impl<'a> Iterator for LinePreprocessor<'a> { line_is_comment_only = false; } - // Allow a comment to follow a docstring. - if !matches!( - token, - TokenKind::String { .. } | TokenKind::NonLogicalNewline | TokenKind::Comment - ) { + // A docstring line is composed only of the docstring (TokenKind::String) and trivia tokens. + // (If a comment follows a docstring, we still count the line as a docstring) + if token != TokenKind::String && !token.is_trivia() { is_docstring = false; } @@ -446,6 +444,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { TokenKind::Newline | TokenKind::NonLogicalNewline if parens == 0 => { let last_token_range = *range; + // For a line to be a docstring, the first token must be a string (since indents are ignored) if first_token != Some(TokenKind::String) { is_docstring = false; } From 3f3ccfa8f302daa1bb20422bb5116cd889df3a83 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:11:12 +0900 Subject: [PATCH 075/122] Replace unwrap by expect. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 58e49e78518b4..675e7cfcb47d5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -449,7 +449,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { is_docstring = false; } - let first_range = first_token_range.unwrap(); + let first_range = first_token_range + .expect("that Newline cannot be the first token of a line (there is at least a NonLogicalNewline token)"); let range = TextRange::new( self.locator.line_start(first_range.start()), @@ -471,9 +472,11 @@ impl<'a> Iterator for LinePreprocessor<'a> { } let logical_line = LogicalLineInfo { - first_token: first_token.unwrap(), + first_token: first_token + .expect("that Newline cannot be the first token of a line (there is at least a NonLogicalNewline token)"), first_token_range: first_range, - last_token: last_token.unwrap(), + last_token: last_token + .expect("that Newline cannot be the first token of a line (there is at least a NonLogicalNewline token)"), last_token_range, is_comment_only: line_is_comment_only, is_docstring, From 12d80ed6bd6975f886baee4219943c07a574f655 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:12:24 +0900 Subject: [PATCH 076/122] check_content -> check_lines --- crates/ruff_linter/src/checkers/tokens.rs | 2 +- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ruff_linter/src/checkers/tokens.rs b/crates/ruff_linter/src/checkers/tokens.rs index 4c902ed9ac4a7..c5e43e194d0f6 100644 --- a/crates/ruff_linter/src/checkers/tokens.rs +++ b/crates/ruff_linter/src/checkers/tokens.rs @@ -45,7 +45,7 @@ pub(crate) fn check_tokens( Rule::BlankLinesBeforeNestedDefinition, ]) { let mut blank_lines_checker = BlankLinesChecker::default(); - blank_lines_checker.check_content(tokens, locator, stylist, &mut diagnostics); + blank_lines_checker.check_lines(tokens, locator, stylist, &mut diagnostics); } if settings.rules.enabled(Rule::BlanketNOQA) { diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 675e7cfcb47d5..a4007fbbef6ff 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -504,7 +504,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { impl BlankLinesChecker { /// E301, E302, E303, E304, E305, E306 #[allow(clippy::too_many_arguments)] - pub(crate) fn check_content( + pub(crate) fn check_lines( &mut self, tokens: &[LexResult], locator: &Locator, From 86c473cbff2528cdd0d1655cc52157784126d835 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:14:53 +0900 Subject: [PATCH 077/122] Move BlankLinesChecker struct declaration closer to its implementation block. --- .../rules/pycodestyle/rules/blank_lines.rs | 67 ++++++++++--------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index a4007fbbef6ff..835f5ed5000b7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -18,44 +18,11 @@ use ruff_text_size::TextSize; use crate::checkers::logical_lines::expand_indent; -/// Contains variables used for the linting of blank lines. -#[derive(Debug, Default)] -#[allow(clippy::struct_excessive_bools)] -pub(crate) struct BlankLinesChecker { - follows: Follows, - fn_status: Status, - class_status: Status, - /// First line that is not a comment. - is_not_first_logical_line: bool, - /// Used for the fix in case a comment separates two non-comment logical lines to make the comment "stick" - /// to the second line instead of the first. - last_non_comment_line_end: TextSize, - previous_unindented_token: Option, -} - /// Number of blank lines around top level classes and functions. const BLANK_LINES_TOP_LEVEL: u32 = 2; /// Number of blank lines around methods and nested classes and functions. const BLANK_LINES_METHOD_LEVEL: u32 = 1; -#[derive(Copy, Clone, Debug, Default)] -enum Follows { - #[default] - Other, - Decorator, - Def, - Docstring, -} -#[derive(Copy, Clone, Debug, Default)] -enum Status { - /// Stores the indent level where the nesting started. - Inside(usize), - /// This is used to rectify a Inside switched to a Outside because of a dedented comment. - CommentAfter(usize), - #[default] - Outside, -} - /// ## What it does /// Checks for missing blank lines between methods of a class. /// @@ -501,6 +468,40 @@ impl<'a> Iterator for LinePreprocessor<'a> { } } +#[derive(Copy, Clone, Debug, Default)] +enum Follows { + #[default] + Other, + Decorator, + Def, + Docstring, +} + +#[derive(Copy, Clone, Debug, Default)] +enum Status { + /// Stores the indent level where the nesting started. + Inside(usize), + /// This is used to rectify a Inside switched to a Outside because of a dedented comment. + CommentAfter(usize), + #[default] + Outside, +} + +/// Contains variables used for the linting of blank lines. +#[derive(Debug, Default)] +#[allow(clippy::struct_excessive_bools)] +pub(crate) struct BlankLinesChecker { + follows: Follows, + fn_status: Status, + class_status: Status, + /// First line that is not a comment. + is_not_first_logical_line: bool, + /// Used for the fix in case a comment separates two non-comment logical lines to make the comment "stick" + /// to the second line instead of the first. + last_non_comment_line_end: TextSize, + previous_unindented_token: Option, +} + impl BlankLinesChecker { /// E301, E302, E303, E304, E305, E306 #[allow(clippy::too_many_arguments)] From c347761ef1d5a03b7d6758cc884fd124caada35b Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:18:30 +0900 Subject: [PATCH 078/122] Remove unnecessary clip allow. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 835f5ed5000b7..b92695f3be428 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -489,7 +489,6 @@ enum Status { /// Contains variables used for the linting of blank lines. #[derive(Debug, Default)] -#[allow(clippy::struct_excessive_bools)] pub(crate) struct BlankLinesChecker { follows: Follows, fn_status: Status, From 820480f699a9fd27f6753ce1b8c2129f98457d4c Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:21:44 +0900 Subject: [PATCH 079/122] Passing TokenKind by value. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index b92695f3be428..e00be903418a2 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -313,12 +313,12 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { /// Returns `true` if the token is a top level token. /// It is sufficient to test for Class and Def since the `LinePreprocessor` ignores Async tokens. -fn is_top_level_token(token: &Option) -> bool { +fn is_top_level_token(token: Option) -> bool { matches!(&token, Some(TokenKind::Class | TokenKind::Def)) } /// Returns `true` if the token is At, Async, Class or Def -fn is_top_level_token_or_decorator(token: &TokenKind) -> bool { +fn is_top_level_token_or_decorator(token: TokenKind) -> bool { matches!(&token, TokenKind::Class | TokenKind::Def | TokenKind::At) } @@ -613,7 +613,7 @@ impl BlankLinesChecker { // Only trigger on non-indented classes and functions (for example functions within an if are ignored) && line.indent_level == 0 // Only apply to functions or classes. - && is_top_level_token_or_decorator(&line.first_token) + && is_top_level_token_or_decorator(line.first_token) { // E302 let mut diagnostic = Diagnostic::new( @@ -681,10 +681,10 @@ impl BlankLinesChecker { } if line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL - && is_top_level_token(&self.previous_unindented_token) + && is_top_level_token(self.previous_unindented_token) && line.indent_level == 0 && !line.is_comment_only - && !is_top_level_token_or_decorator(&line.first_token) + && !is_top_level_token_or_decorator(line.first_token) { // E305 let mut diagnostic = Diagnostic::new( @@ -709,7 +709,7 @@ impl BlankLinesChecker { if line.preceding_blank_lines == 0 // Only apply to nested functions. && matches!(self.fn_status, Status::Inside(_)) - && is_top_level_token_or_decorator(&line.first_token) + && is_top_level_token_or_decorator(line.first_token) // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !matches!(self.follows, Follows::Decorator) // The class's docstring can directly precede the first function. From 3df1f1b0111398a32d0ad67e3dfb254f514d7df4 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:23:14 +0900 Subject: [PATCH 080/122] Remove unnecessary clip allow (too many arguments). --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index e00be903418a2..74281080814b8 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -503,7 +503,6 @@ pub(crate) struct BlankLinesChecker { impl BlankLinesChecker { /// E301, E302, E303, E304, E305, E306 - #[allow(clippy::too_many_arguments)] pub(crate) fn check_lines( &mut self, tokens: &[LexResult], From cce4d008ba4420dc7e2a71f4e75637ed917b527b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ho=C3=ABl=20Bagard?= <34478245+hoel-bagard@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:35:35 +0900 Subject: [PATCH 081/122] Update crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs Co-authored-by: Micha Reiser --- .../rules/pycodestyle/rules/blank_lines.rs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 74281080814b8..0258c50553ab2 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -538,14 +538,27 @@ impl BlankLinesChecker { ) { let indent_size: usize = 4; - if let Status::Inside(nesting_indent) = self.class_status { - if line.indent_level < nesting_indent { - if line.is_comment_only { - self.class_status = Status::CommentAfter(nesting_indent); - } else { + match self.class_status { + Status::Inside(nesting_indent) => { + if line.indent_level < nesting_indent { + if line.is_comment_only { + self.class_status = Status::CommentAfter(nesting_indent); + } else { + self.class_status = Status::Outside; + } + } + } + Status::CommentAfter(indent) => { + if !line.is_comment_only { + if line.indent_level >= indent { + self.class_status = Status::Inside(indent); + } self.class_status = Status::Outside; } } + Status::Outside => { + // Nothing to do + } } if let Status::Inside(nesting_indent) = self.fn_status { @@ -567,13 +580,6 @@ impl BlankLinesChecker { } self.fn_status = Status::Outside; } - - if let Status::CommentAfter(indent) = self.class_status { - if line.indent_level >= indent { - self.class_status = Status::Inside(indent); - } - self.class_status = Status::Outside; - } } // Don't expect blank lines before the first non comment line. From 92fca2492dcab3163b594590b72def6cf4ffc2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ho=C3=ABl=20Bagard?= <34478245+hoel-bagard@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:45:45 +0900 Subject: [PATCH 082/122] Update crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs Co-authored-by: Micha Reiser --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 0258c50553ab2..9a740a74fdcb9 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -584,7 +584,7 @@ impl BlankLinesChecker { // Don't expect blank lines before the first non comment line. if self.is_not_first_logical_line { - if line.preceding_blank_lines == 0 + && line.first_token == Tok::Def // Only applies to methods. && line.first_token == TokenKind::Def && matches!(self.class_status, Status::Inside(_)) From 8985a398e34b14fe3060668c653fdfbb6bcd040b Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:48:05 +0900 Subject: [PATCH 083/122] Revert "Update crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs" This reverts commit 92fca2492dcab3163b594590b72def6cf4ffc2b1. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 9a740a74fdcb9..0258c50553ab2 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -584,7 +584,7 @@ impl BlankLinesChecker { // Don't expect blank lines before the first non comment line. if self.is_not_first_logical_line { - && line.first_token == Tok::Def + if line.preceding_blank_lines == 0 // Only applies to methods. && line.first_token == TokenKind::Def && matches!(self.class_status, Status::Inside(_)) From 3941e8f99f497be41dcc45e87d069fa9bb5938e8 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:48:21 +0900 Subject: [PATCH 084/122] Fix visual indent. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 0258c50553ab2..f0a63adf39e9b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -586,7 +586,7 @@ impl BlankLinesChecker { if self.is_not_first_logical_line { if line.preceding_blank_lines == 0 // Only applies to methods. - && line.first_token == TokenKind::Def + && line.first_token == TokenKind::Def && matches!(self.class_status, Status::Inside(_)) // The class/parent method's docstring can directly precede the def. && !matches!(self.follows, Follows::Docstring) From 1831879a2d86be1a7305f77f8c6ddc069b995d08 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:51:20 +0900 Subject: [PATCH 085/122] Fix visual indent. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index f0a63adf39e9b..35be515c790dd 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -618,7 +618,7 @@ impl BlankLinesChecker { // Only trigger on non-indented classes and functions (for example functions within an if are ignored) && line.indent_level == 0 // Only apply to functions or classes. - && is_top_level_token_or_decorator(line.first_token) + && is_top_level_token_or_decorator(line.first_token) { // E302 let mut diagnostic = Diagnostic::new( From 52589e4335251639258c2cea7c7ce2aba1f083c1 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 22:53:13 +0900 Subject: [PATCH 086/122] Revert unrelated variable name change. --- crates/ruff_linter/src/checkers/logical_lines.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/src/checkers/logical_lines.rs b/crates/ruff_linter/src/checkers/logical_lines.rs index 6cf8f4c4f80da..9be4c13ef7c52 100644 --- a/crates/ruff_linter/src/checkers/logical_lines.rs +++ b/crates/ruff_linter/src/checkers/logical_lines.rs @@ -38,7 +38,7 @@ pub(crate) fn check_logical_lines( ) -> Vec { let mut context = LogicalLinesContext::new(settings); - let mut non_comment_prev_line = None; + let mut prev_line = None; let mut prev_indent_level = None; let indent_char = stylist.indentation().as_char(); @@ -90,7 +90,7 @@ pub(crate) fn check_logical_lines( for kind in indentation( &line, - non_comment_prev_line.as_ref(), + prev_line.as_ref(), indent_char, indent_level, prev_indent_level, @@ -102,7 +102,7 @@ pub(crate) fn check_logical_lines( } if !line.is_comment_only() { - non_comment_prev_line = Some(line); + prev_line = Some(line); prev_indent_level = Some(indent_level); } } From 4f2c318a7790de8a9fdec19e4b40fab5bcf5dcef Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 23:16:55 +0900 Subject: [PATCH 087/122] Add tests with indentation made of 2 spaces, tabs and mixed spaces and tabs --- .../test/fixtures/pycodestyle/E30.py | 24 ++ ...ules__pycodestyle__tests__E301_E30.py.snap | 52 +-- ...ules__pycodestyle__tests__E302_E30.py.snap | 270 +++++++-------- ...ules__pycodestyle__tests__E303_E30.py.snap | 196 +++++------ ...ules__pycodestyle__tests__E304_E30.py.snap | 26 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 324 +++++++++--------- 7 files changed, 521 insertions(+), 497 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 0176db51a7426..8efe185efc26e 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -393,6 +393,30 @@ async def function1(): #end +# no error +async def function1(): + await function2() + async with function3(): + pass +# end + + +# no error +async def function1(): + await function2() + async with function3(): + pass +# end + + +# no error +async def function1(): + await function2() + async with function3(): + pass +# end + + # E301 class Class(object): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index b3f278d455c16..8920d9e0b93c7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:401:5: E301 [*] Expected 1 blank line, found 0 +E30.py:425:5: E301 [*] Expected 1 blank line, found 0 | -399 | def func1(): -400 | pass -401 | def func2(): +423 | def func1(): +424 | pass +425 | def func2(): | ^^^ E301 -402 | pass -403 | # end +426 | pass +427 | # end | = help: Add missing blank line(s) ℹ Safe fix -398 398 | -399 399 | def func1(): -400 400 | pass - 401 |+ -401 402 | def func2(): -402 403 | pass -403 404 | # end +422 422 | +423 423 | def func1(): +424 424 | pass + 425 |+ +425 426 | def func2(): +426 427 | pass +427 428 | # end -E30.py:412:5: E301 [*] Expected 1 blank line, found 0 +E30.py:436:5: E301 [*] Expected 1 blank line, found 0 | -410 | pass -411 | # comment -412 | def fn2(): +434 | pass +435 | # comment +436 | def fn2(): | ^^^ E301 -413 | pass -414 | # end +437 | pass +438 | # end | = help: Add missing blank line(s) ℹ Safe fix -408 408 | -409 409 | def fn1(): -410 410 | pass - 411 |+ -411 412 | # comment -412 413 | def fn2(): -413 414 | pass +432 432 | +433 433 | def fn1(): +434 434 | pass + 435 |+ +435 436 | # comment +436 437 | def fn2(): +437 438 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 3da181d09149d..39801181c2793 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,186 +1,186 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:419:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:443:1: E302 [*] Expected 2 blank lines, found 0 | -417 | # E302 -418 | """Main module.""" -419 | def fn(): +441 | # E302 +442 | """Main module.""" +443 | def fn(): | ^^^ E302 -420 | pass -421 | # end +444 | pass +445 | # end | = help: Add missing blank line(s) ℹ Safe fix -416 416 | -417 417 | # E302 -418 418 | """Main module.""" - 419 |+ - 420 |+ -419 421 | def fn(): -420 422 | pass -421 423 | # end - -E30.py:426:1: E302 [*] Expected 2 blank lines, found 0 - | -424 | # E302 -425 | import sys -426 | def get_sys_path(): +440 440 | +441 441 | # E302 +442 442 | """Main module.""" + 443 |+ + 444 |+ +443 445 | def fn(): +444 446 | pass +445 447 | # end + +E30.py:450:1: E302 [*] Expected 2 blank lines, found 0 + | +448 | # E302 +449 | import sys +450 | def get_sys_path(): | ^^^ E302 -427 | return sys.path -428 | # end +451 | return sys.path +452 | # end | = help: Add missing blank line(s) ℹ Safe fix -423 423 | -424 424 | # E302 -425 425 | import sys - 426 |+ - 427 |+ -426 428 | def get_sys_path(): -427 429 | return sys.path -428 430 | # end - -E30.py:435:1: E302 [*] Expected 2 blank lines, found 1 - | -433 | pass -434 | -435 | def b(): +447 447 | +448 448 | # E302 +449 449 | import sys + 450 |+ + 451 |+ +450 452 | def get_sys_path(): +451 453 | return sys.path +452 454 | # end + +E30.py:459:1: E302 [*] Expected 2 blank lines, found 1 + | +457 | pass +458 | +459 | def b(): | ^^^ E302 -436 | pass -437 | # end +460 | pass +461 | # end | = help: Add missing blank line(s) ℹ Safe fix -432 432 | def a(): -433 433 | pass -434 434 | - 435 |+ -435 436 | def b(): -436 437 | pass -437 438 | # end - -E30.py:446:1: E302 [*] Expected 2 blank lines, found 1 - | -444 | # comment -445 | -446 | def b(): +456 456 | def a(): +457 457 | pass +458 458 | + 459 |+ +459 460 | def b(): +460 461 | pass +461 462 | # end + +E30.py:470:1: E302 [*] Expected 2 blank lines, found 1 + | +468 | # comment +469 | +470 | def b(): | ^^^ E302 -447 | pass -448 | # end +471 | pass +472 | # end | = help: Add missing blank line(s) ℹ Safe fix -441 441 | def a(): -442 442 | pass -443 443 | - 444 |+ -444 445 | # comment -445 446 | -446 447 | def b(): - -E30.py:455:1: E302 [*] Expected 2 blank lines, found 1 - | -453 | pass -454 | -455 | async def b(): +465 465 | def a(): +466 466 | pass +467 467 | + 468 |+ +468 469 | # comment +469 470 | +470 471 | def b(): + +E30.py:479:1: E302 [*] Expected 2 blank lines, found 1 + | +477 | pass +478 | +479 | async def b(): | ^^^^^ E302 -456 | pass -457 | # end +480 | pass +481 | # end | = help: Add missing blank line(s) ℹ Safe fix -452 452 | def a(): -453 453 | pass -454 454 | - 455 |+ -455 456 | async def b(): -456 457 | pass -457 458 | # end - -E30.py:464:1: E302 [*] Expected 2 blank lines, found 1 - | -462 | pass -463 | -464 | async def x(y: int = 1): +476 476 | def a(): +477 477 | pass +478 478 | + 479 |+ +479 480 | async def b(): +480 481 | pass +481 482 | # end + +E30.py:488:1: E302 [*] Expected 2 blank lines, found 1 + | +486 | pass +487 | +488 | async def x(y: int = 1): | ^^^^^ E302 -465 | pass -466 | # end +489 | pass +490 | # end | = help: Add missing blank line(s) ℹ Safe fix -461 461 | async def x(): -462 462 | pass -463 463 | - 464 |+ -464 465 | async def x(y: int = 1): -465 466 | pass -466 467 | # end - -E30.py:472:1: E302 [*] Expected 2 blank lines, found 0 - | -470 | def bar(): -471 | pass -472 | def baz(): pass +485 485 | async def x(): +486 486 | pass +487 487 | + 488 |+ +488 489 | async def x(y: int = 1): +489 490 | pass +490 491 | # end + +E30.py:496:1: E302 [*] Expected 2 blank lines, found 0 + | +494 | def bar(): +495 | pass +496 | def baz(): pass | ^^^ E302 -473 | # end +497 | # end | = help: Add missing blank line(s) ℹ Safe fix -469 469 | # E302 -470 470 | def bar(): -471 471 | pass - 472 |+ - 473 |+ -472 474 | def baz(): pass -473 475 | # end -474 476 | - -E30.py:478:1: E302 [*] Expected 2 blank lines, found 0 - | -476 | # E302 -477 | def bar(): pass -478 | def baz(): +493 493 | # E302 +494 494 | def bar(): +495 495 | pass + 496 |+ + 497 |+ +496 498 | def baz(): pass +497 499 | # end +498 500 | + +E30.py:502:1: E302 [*] Expected 2 blank lines, found 0 + | +500 | # E302 +501 | def bar(): pass +502 | def baz(): | ^^^ E302 -479 | pass -480 | # end +503 | pass +504 | # end | = help: Add missing blank line(s) ℹ Safe fix -475 475 | -476 476 | # E302 -477 477 | def bar(): pass - 478 |+ - 479 |+ -478 480 | def baz(): -479 481 | pass -480 482 | # end - -E30.py:488:1: E302 [*] Expected 2 blank lines, found 1 - | -487 | # comment -488 | @decorator +499 499 | +500 500 | # E302 +501 501 | def bar(): pass + 502 |+ + 503 |+ +502 504 | def baz(): +503 505 | pass +504 506 | # end + +E30.py:512:1: E302 [*] Expected 2 blank lines, found 1 + | +511 | # comment +512 | @decorator | ^ E302 -489 | def g(): -490 | pass +513 | def g(): +514 | pass | = help: Add missing blank line(s) ℹ Safe fix -484 484 | def f(): -485 485 | pass -486 486 | - 487 |+ -487 488 | # comment -488 489 | @decorator -489 490 | def g(): +508 508 | def f(): +509 509 | pass +510 510 | + 511 |+ +511 512 | # comment +512 513 | @decorator +513 514 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index b25448054562b..1c37309ec21e5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,163 +1,163 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:499:5: E303 [*] Too many blank lines (2) +E30.py:523:5: E303 [*] Too many blank lines (2) | -499 | # arbitrary comment +523 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -500 | -501 | def inner(): # E306 not expected +524 | +525 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -495 495 | def fn(): -496 496 | _ = None -497 497 | -498 |- -499 498 | # arbitrary comment -500 499 | -501 500 | def inner(): # E306 not expected +519 519 | def fn(): +520 520 | _ = None +521 521 | +522 |- +523 522 | # arbitrary comment +524 523 | +525 524 | def inner(): # E306 not expected -E30.py:511:5: E303 [*] Too many blank lines (2) +E30.py:535:5: E303 [*] Too many blank lines (2) | -511 | # arbitrary comment +535 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -512 | def inner(): # E306 not expected -513 | pass +536 | def inner(): # E306 not expected +537 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -507 507 | def fn(): -508 508 | _ = None -509 509 | -510 |- -511 510 | # arbitrary comment -512 511 | def inner(): # E306 not expected -513 512 | pass +531 531 | def fn(): +532 532 | _ = None +533 533 | +534 |- +535 534 | # arbitrary comment +536 535 | def inner(): # E306 not expected +537 536 | pass -E30.py:522:1: E303 [*] Too many blank lines (3) +E30.py:546:1: E303 [*] Too many blank lines (3) | -522 | print() +546 | print() | ^^^^^ E303 -523 | # end +547 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -518 518 | print() -519 519 | -520 520 | -521 |- -522 521 | print() -523 522 | # end -524 523 | +542 542 | print() +543 543 | +544 544 | +545 |- +546 545 | print() +547 546 | # end +548 547 | -E30.py:531:1: E303 [*] Too many blank lines (3) +E30.py:555:1: E303 [*] Too many blank lines (3) | -531 | # comment +555 | # comment | ^^^^^^^^^ E303 -532 | -533 | print() +556 | +557 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -527 527 | print() -528 528 | -529 529 | -530 |- -531 530 | # comment -532 531 | -533 532 | print() +551 551 | print() +552 552 | +553 553 | +554 |- +555 554 | # comment +556 555 | +557 556 | print() -E30.py:542:5: E303 [*] Too many blank lines (2) +E30.py:566:5: E303 [*] Too many blank lines (2) | -542 | # comment +566 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -538 538 | def a(): -539 539 | print() -540 540 | -541 |- -542 541 | # comment -543 542 | -544 543 | +562 562 | def a(): +563 563 | print() +564 564 | +565 |- +566 565 | # comment +567 566 | +568 567 | -E30.py:545:5: E303 [*] Too many blank lines (2) +E30.py:569:5: E303 [*] Too many blank lines (2) | -545 | # another comment +569 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -546 | -547 | print() +570 | +571 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -541 541 | -542 542 | # comment -543 543 | -544 |- -545 544 | # another comment -546 545 | -547 546 | print() - -E30.py:556:1: E303 [*] Too many blank lines (3) - | -556 | / """This class docstring comes on line 5. -557 | | It gives error E303: too many blank lines (3) -558 | | """ +565 565 | +566 566 | # comment +567 567 | +568 |- +569 568 | # another comment +570 569 | +571 570 | print() + +E30.py:580:1: E303 [*] Too many blank lines (3) + | +580 | / """This class docstring comes on line 5. +581 | | It gives error E303: too many blank lines (3) +582 | | """ | |___^ E303 -559 | # end +583 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -552 552 | #!python -553 553 | -554 554 | -555 |- -556 555 | """This class docstring comes on line 5. -557 556 | It gives error E303: too many blank lines (3) -558 557 | """ +576 576 | #!python +577 577 | +578 578 | +579 |- +580 579 | """This class docstring comes on line 5. +581 580 | It gives error E303: too many blank lines (3) +582 581 | """ -E30.py:568:5: E303 [*] Too many blank lines (2) +E30.py:592:5: E303 [*] Too many blank lines (2) | -568 | def b(self): +592 | def b(self): | ^^^ E303 -569 | pass -570 | # end +593 | pass +594 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -564 564 | def a(self): -565 565 | pass -566 566 | -567 |- -568 567 | def b(self): -569 568 | pass -570 569 | # end +588 588 | def a(self): +589 589 | pass +590 590 | +591 |- +592 591 | def b(self): +593 592 | pass +594 593 | # end -E30.py:578:5: E303 [*] Too many blank lines (2) +E30.py:602:5: E303 [*] Too many blank lines (2) | -578 | a = 2 +602 | a = 2 | ^ E303 -579 | # end +603 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -574 574 | if True: -575 575 | a = 1 -576 576 | -577 |- -578 577 | a = 2 -579 578 | # end -580 579 | +598 598 | if True: +599 599 | a = 1 +600 600 | +601 |- +602 601 | a = 2 +603 602 | # end +604 603 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index fd75e6120a76f..08ce97ff5d4ce 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,24 +1,24 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:585:1: E304 [*] blank lines found after function decorator +E30.py:609:1: E304 [*] blank lines found after function decorator | -583 | @decorator -584 | -585 | def function(): +607 | @decorator +608 | +609 | def function(): | ^^^ E304 -586 | pass -587 | # end +610 | pass +611 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -581 581 | -582 582 | # E304 -583 583 | @decorator -584 |- -585 584 | def function(): -586 585 | pass -587 586 | # end +605 605 | +606 606 | # E304 +607 607 | @decorator +608 |- +609 608 | def function(): +610 609 | pass +611 610 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 942fdb202bff6..3a5e99c6fa487 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:597:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:621:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -596 | # another comment -597 | fn() +620 | # another comment +621 | fn() | ^^ E305 -598 | # end +622 | # end | = help: Add missing blank line(s) ℹ Safe fix -594 594 | # comment -595 595 | -596 596 | # another comment - 597 |+ - 598 |+ -597 599 | fn() -598 600 | # end -599 601 | +618 618 | # comment +619 619 | +620 620 | # another comment + 621 |+ + 622 |+ +621 623 | fn() +622 624 | # end +623 625 | -E30.py:608:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:632:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -607 | # another comment -608 | a = 1 +631 | # another comment +632 | a = 1 | ^ E305 -609 | # end +633 | # end | = help: Add missing blank line(s) ℹ Safe fix -605 605 | # comment -606 606 | -607 607 | # another comment - 608 |+ - 609 |+ -608 610 | a = 1 -609 611 | # end -610 612 | +629 629 | # comment +630 630 | +631 631 | # another comment + 632 |+ + 633 |+ +632 634 | a = 1 +633 635 | # end +634 636 | -E30.py:620:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:644:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -618 | # another comment -619 | -620 | try: +642 | # another comment +643 | +644 | try: | ^^^ E305 -621 | fn() -622 | except Exception: +645 | fn() +646 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -617 617 | -618 618 | # another comment -619 619 | - 620 |+ -620 621 | try: -621 622 | fn() -622 623 | except Exception: +641 641 | +642 642 | # another comment +643 643 | + 644 |+ +644 645 | try: +645 646 | fn() +646 647 | except Exception: -E30.py:632:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:656:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -631 | # Two spaces before comments, too. -632 | if a(): +655 | # Two spaces before comments, too. +656 | if a(): | ^^ E305 -633 | a() -634 | # end +657 | a() +658 | # end | = help: Add missing blank line(s) ℹ Safe fix -629 629 | print -630 630 | -631 631 | # Two spaces before comments, too. - 632 |+ - 633 |+ -632 634 | if a(): -633 635 | a() -634 636 | # end +653 653 | print +654 654 | +655 655 | # Two spaces before comments, too. + 656 |+ + 657 |+ +656 658 | if a(): +657 659 | a() +658 660 | # end -E30.py:645:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:669:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -643 | blah, blah -644 | -645 | if __name__ == '__main__': +667 | blah, blah +668 | +669 | if __name__ == '__main__': | ^^ E305 -646 | main() -647 | # end +670 | main() +671 | # end | = help: Add missing blank line(s) ℹ Safe fix -642 642 | def main(): -643 643 | blah, blah -644 644 | - 645 |+ -645 646 | if __name__ == '__main__': -646 647 | main() -647 648 | # end +666 666 | def main(): +667 667 | blah, blah +668 668 | + 669 |+ +669 670 | if __name__ == '__main__': +670 671 | main() +671 672 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 44b512584a493..46d28bc5acfbd 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,223 +1,223 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:653:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:677:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -651 | def a(): -652 | x = 1 -653 | def b(): +675 | def a(): +676 | x = 1 +677 | def b(): | ^^^ E306 -654 | pass -655 | # end +678 | pass +679 | # end | = help: Add missing blank line ℹ Safe fix -650 650 | # E306:3:5 -651 651 | def a(): -652 652 | x = 1 - 653 |+ -653 654 | def b(): -654 655 | pass -655 656 | # end - -E30.py:661:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -659 | async def a(): -660 | x = 1 -661 | def b(): +674 674 | # E306:3:5 +675 675 | def a(): +676 676 | x = 1 + 677 |+ +677 678 | def b(): +678 679 | pass +679 680 | # end + +E30.py:685:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +683 | async def a(): +684 | x = 1 +685 | def b(): | ^^^ E306 -662 | pass -663 | # end +686 | pass +687 | # end | = help: Add missing blank line ℹ Safe fix -658 658 | #: E306:3:5 -659 659 | async def a(): -660 660 | x = 1 - 661 |+ -661 662 | def b(): -662 663 | pass -663 664 | # end - -E30.py:669:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -667 | def a(): -668 | x = 2 -669 | def b(): +682 682 | #: E306:3:5 +683 683 | async def a(): +684 684 | x = 1 + 685 |+ +685 686 | def b(): +686 687 | pass +687 688 | # end + +E30.py:693:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +691 | def a(): +692 | x = 2 +693 | def b(): | ^^^ E306 -670 | x = 1 -671 | def c(): +694 | x = 1 +695 | def c(): | = help: Add missing blank line ℹ Safe fix -666 666 | #: E306:3:5 E306:5:9 -667 667 | def a(): -668 668 | x = 2 - 669 |+ -669 670 | def b(): -670 671 | x = 1 -671 672 | def c(): - -E30.py:671:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -669 | def b(): -670 | x = 1 -671 | def c(): +690 690 | #: E306:3:5 E306:5:9 +691 691 | def a(): +692 692 | x = 2 + 693 |+ +693 694 | def b(): +694 695 | x = 1 +695 696 | def c(): + +E30.py:695:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +693 | def b(): +694 | x = 1 +695 | def c(): | ^^^ E306 -672 | pass -673 | # end +696 | pass +697 | # end | = help: Add missing blank line ℹ Safe fix -668 668 | x = 2 -669 669 | def b(): -670 670 | x = 1 - 671 |+ -671 672 | def c(): -672 673 | pass -673 674 | # end - -E30.py:679:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -677 | def a(): -678 | x = 1 -679 | class C: +692 692 | x = 2 +693 693 | def b(): +694 694 | x = 1 + 695 |+ +695 696 | def c(): +696 697 | pass +697 698 | # end + +E30.py:703:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +701 | def a(): +702 | x = 1 +703 | class C: | ^^^^^ E306 -680 | pass -681 | x = 2 +704 | pass +705 | x = 2 | = help: Add missing blank line ℹ Safe fix -676 676 | # E306:3:5 E306:6:5 -677 677 | def a(): -678 678 | x = 1 - 679 |+ -679 680 | class C: -680 681 | pass -681 682 | x = 2 - -E30.py:682:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -680 | pass -681 | x = 2 -682 | def b(): +700 700 | # E306:3:5 E306:6:5 +701 701 | def a(): +702 702 | x = 1 + 703 |+ +703 704 | class C: +704 705 | pass +705 706 | x = 2 + +E30.py:706:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +704 | pass +705 | x = 2 +706 | def b(): | ^^^ E306 -683 | pass -684 | # end +707 | pass +708 | # end | = help: Add missing blank line ℹ Safe fix -679 679 | class C: -680 680 | pass -681 681 | x = 2 - 682 |+ -682 683 | def b(): -683 684 | pass -684 685 | # end - -E30.py:691:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -689 | def bar(): -690 | pass -691 | def baz(): pass +703 703 | class C: +704 704 | pass +705 705 | x = 2 + 706 |+ +706 707 | def b(): +707 708 | pass +708 709 | # end + +E30.py:715:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +713 | def bar(): +714 | pass +715 | def baz(): pass | ^^^ E306 -692 | # end +716 | # end | = help: Add missing blank line ℹ Safe fix -688 688 | def foo(): -689 689 | def bar(): -690 690 | pass - 691 |+ -691 692 | def baz(): pass -692 693 | # end -693 694 | - -E30.py:698:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -696 | def foo(): -697 | def bar(): pass -698 | def baz(): +712 712 | def foo(): +713 713 | def bar(): +714 714 | pass + 715 |+ +715 716 | def baz(): pass +716 717 | # end +717 718 | + +E30.py:722:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +720 | def foo(): +721 | def bar(): pass +722 | def baz(): | ^^^ E306 -699 | pass -700 | # end +723 | pass +724 | # end | = help: Add missing blank line ℹ Safe fix -695 695 | # E306:3:5 -696 696 | def foo(): -697 697 | def bar(): pass - 698 |+ -698 699 | def baz(): -699 700 | pass -700 701 | # end - -E30.py:706:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -704 | def a(): -705 | x = 2 -706 | @decorator +719 719 | # E306:3:5 +720 720 | def foo(): +721 721 | def bar(): pass + 722 |+ +722 723 | def baz(): +723 724 | pass +724 725 | # end + +E30.py:730:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +728 | def a(): +729 | x = 2 +730 | @decorator | ^ E306 -707 | def b(): -708 | pass +731 | def b(): +732 | pass | = help: Add missing blank line ℹ Safe fix -703 703 | # E306 -704 704 | def a(): -705 705 | x = 2 - 706 |+ -706 707 | @decorator -707 708 | def b(): -708 709 | pass - -E30.py:715:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -713 | def a(): -714 | x = 2 -715 | @decorator +727 727 | # E306 +728 728 | def a(): +729 729 | x = 2 + 730 |+ +730 731 | @decorator +731 732 | def b(): +732 733 | pass + +E30.py:739:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +737 | def a(): +738 | x = 2 +739 | @decorator | ^ E306 -716 | async def b(): -717 | pass +740 | async def b(): +741 | pass | = help: Add missing blank line ℹ Safe fix -712 712 | # E306 -713 713 | def a(): -714 714 | x = 2 - 715 |+ -715 716 | @decorator -716 717 | async def b(): -717 718 | pass - -E30.py:724:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -722 | def a(): -723 | x = 2 -724 | async def b(): +736 736 | # E306 +737 737 | def a(): +738 738 | x = 2 + 739 |+ +739 740 | @decorator +740 741 | async def b(): +741 742 | pass + +E30.py:748:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +746 | def a(): +747 | x = 2 +748 | async def b(): | ^^^^^ E306 -725 | pass -726 | # end +749 | pass +750 | # end | = help: Add missing blank line ℹ Safe fix -721 721 | # E306 -722 722 | def a(): -723 723 | x = 2 - 724 |+ -724 725 | async def b(): -725 726 | pass -726 727 | # end +745 745 | # E306 +746 746 | def a(): +747 747 | x = 2 + 748 |+ +748 749 | async def b(): +749 750 | pass +750 751 | # end From 94c9babffd6cbd3c807a173cd671f25553389bca Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 23:21:52 +0900 Subject: [PATCH 088/122] Do not use an hard coded indent size. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 35be515c790dd..7ff87140c8981 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -536,11 +536,9 @@ impl BlankLinesChecker { stylist: &Stylist, diagnostics: &mut Vec, ) { - let indent_size: usize = 4; - match self.class_status { Status::Inside(nesting_indent) => { - if line.indent_level < nesting_indent { + if line.indent_level <= nesting_indent { if line.is_comment_only { self.class_status = Status::CommentAfter(nesting_indent); } else { @@ -550,7 +548,7 @@ impl BlankLinesChecker { } Status::CommentAfter(indent) => { if !line.is_comment_only { - if line.indent_level >= indent { + if line.indent_level > indent { self.class_status = Status::Inside(indent); } self.class_status = Status::Outside; @@ -562,7 +560,7 @@ impl BlankLinesChecker { } if let Status::Inside(nesting_indent) = self.fn_status { - if line.indent_level < nesting_indent { + if line.indent_level <= nesting_indent { if line.is_comment_only { self.fn_status = Status::CommentAfter(nesting_indent); } else { @@ -575,7 +573,7 @@ impl BlankLinesChecker { // we need to revert the variables. if !line.is_comment_only { if let Status::CommentAfter(indent) = self.fn_status { - if line.indent_level >= indent { + if line.indent_level > indent { self.fn_status = Status::Inside(indent); } self.fn_status = Status::Outside; @@ -744,7 +742,7 @@ impl BlankLinesChecker { match line.first_token { TokenKind::Class => { if matches!(self.class_status, Status::Outside) { - self.class_status = Status::Inside(line.indent_level + indent_size); + self.class_status = Status::Inside(line.indent_level); } self.follows = Follows::Other; } @@ -753,7 +751,7 @@ impl BlankLinesChecker { } TokenKind::Def | TokenKind::Async => { if matches!(self.fn_status, Status::Outside) { - self.fn_status = Status::Inside(line.indent_level + indent_size); + self.fn_status = Status::Inside(line.indent_level); } self.follows = Follows::Def; } From da11f0284214e25dab5abd086726342edbba5734 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 23:24:34 +0900 Subject: [PATCH 089/122] Fix missing else branch --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 7ff87140c8981..8b18434a14f80 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -575,8 +575,9 @@ impl BlankLinesChecker { if let Status::CommentAfter(indent) = self.fn_status { if line.indent_level > indent { self.fn_status = Status::Inside(indent); + } else { + self.fn_status = Status::Outside; } - self.fn_status = Status::Outside; } } From 531ce0ed5dc9c52387b119427a53ccd87f53f214 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 23:30:55 +0900 Subject: [PATCH 090/122] Remove outdated Async check. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 8b18434a14f80..20fe8eee8525e 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -750,7 +750,7 @@ impl BlankLinesChecker { TokenKind::At => { self.follows = Follows::Decorator; } - TokenKind::Def | TokenKind::Async => { + TokenKind::Def => { if matches!(self.fn_status, Status::Outside) { self.fn_status = Status::Inside(line.indent_level); } From cc81cc3619ff77b0f3fe60a739ea85086616c673 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 23:37:50 +0900 Subject: [PATCH 091/122] Remove unecessary if. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 20fe8eee8525e..d9ed632009fcf 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -763,9 +763,7 @@ impl BlankLinesChecker { } if !line.is_comment_only { - if !self.is_not_first_logical_line { - self.is_not_first_logical_line = true; - } + self.is_not_first_logical_line = true; if line.is_docstring { self.follows = Follows::Docstring; From bdfb1e3ac0bc07e47f779385e63afc6aaf984df4 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 23:43:31 +0900 Subject: [PATCH 092/122] Move follow docstring if block outside of comment only if block. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index d9ed632009fcf..e97c040a64602 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -762,13 +762,13 @@ impl BlankLinesChecker { } } + if line.is_docstring { + self.follows = Follows::Docstring; + } + if !line.is_comment_only { self.is_not_first_logical_line = true; - if line.is_docstring { - self.follows = Follows::Docstring; - } - self.last_non_comment_line_end = line.last_token_range.end(); if line.indent_level == 0 && line.first_token != TokenKind::Comment { From 894bc21efa3aec85a439f1088ce05963138f24c3 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 23:45:34 +0900 Subject: [PATCH 093/122] Remove unecessary comment check condition. Testing for comments here was unnecessary because a logical line starting with a comment is guaranteed to be a comment only line. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index e97c040a64602..d7d24658063a7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -771,7 +771,7 @@ impl BlankLinesChecker { self.last_non_comment_line_end = line.last_token_range.end(); - if line.indent_level == 0 && line.first_token != TokenKind::Comment { + if line.indent_level == 0 { self.previous_unindented_token = Some(line.first_token); } } From 0f659b965f35d504aceea0546dddd88b3ff1f49e Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 10 Jan 2024 23:48:40 +0900 Subject: [PATCH 094/122] Only store the last token's end instead of full range. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index d7d24658063a7..53cc8208c7e69 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -327,7 +327,7 @@ struct LogicalLineInfo { first_token: TokenKind, first_token_range: TextRange, last_token: TokenKind, - last_token_range: TextRange, + last_token_end: TextSize, is_comment_only: bool, is_docstring: bool, indent_level: usize, @@ -409,7 +409,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { parens = parens.saturating_sub(1); } TokenKind::Newline | TokenKind::NonLogicalNewline if parens == 0 => { - let last_token_range = *range; + let last_token_end = range.end(); // For a line to be a docstring, the first token must be a string (since indents are ignored) if first_token != Some(TokenKind::String) { @@ -444,7 +444,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { first_token_range: first_range, last_token: last_token .expect("that Newline cannot be the first token of a line (there is at least a NonLogicalNewline token)"), - last_token_range, + last_token_end, is_comment_only: line_is_comment_only, is_docstring, indent_level, @@ -769,7 +769,7 @@ impl BlankLinesChecker { if !line.is_comment_only { self.is_not_first_logical_line = true; - self.last_non_comment_line_end = line.last_token_range.end(); + self.last_non_comment_line_end = line.last_token_end; if line.indent_level == 0 { self.previous_unindented_token = Some(line.first_token); From 0d3644445b9bc4bc76e190baa3b43de241b648c3 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Thu, 11 Jan 2024 00:10:13 +0900 Subject: [PATCH 095/122] Rename indent_level to indent_length --- .../rules/pycodestyle/rules/blank_lines.rs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 53cc8208c7e69..8d77efa37e0e8 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -330,7 +330,7 @@ struct LogicalLineInfo { last_token_end: TextSize, is_comment_only: bool, is_docstring: bool, - indent_level: usize, + indent_length: usize, blank_lines: u32, preceding_blank_lines: u32, preceding_blank_characters: usize, @@ -424,7 +424,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { first_range.start(), ); - let indent_level = expand_indent(self.locator.slice(range)); + let indent_length = expand_indent(self.locator.slice(range)); // Empty line if first_token == Some(TokenKind::NonLogicalNewline) { @@ -447,7 +447,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { last_token_end, is_comment_only: line_is_comment_only, is_docstring, - indent_level, + indent_length, blank_lines: self.current_blank_lines, preceding_blank_lines: self.previous_blank_lines, preceding_blank_characters: self.current_blank_characters, @@ -510,19 +510,19 @@ impl BlankLinesChecker { stylist: &Stylist, diagnostics: &mut Vec, ) { - let mut prev_indent_level: Option = None; + let mut prev_indent_length: Option = None; let line_preprocessor = LinePreprocessor::new(tokens, locator); for logical_line in line_preprocessor { self.check_line( &logical_line, - prev_indent_level, + prev_indent_length, locator, stylist, diagnostics, ); if !logical_line.is_comment_only { - prev_indent_level = Some(logical_line.indent_level); + prev_indent_length = Some(logical_line.indent_length); } } } @@ -531,14 +531,14 @@ impl BlankLinesChecker { fn check_line( &mut self, line: &LogicalLineInfo, - prev_indent_level: Option, + prev_indent_length: Option, locator: &Locator, stylist: &Stylist, diagnostics: &mut Vec, ) { match self.class_status { Status::Inside(nesting_indent) => { - if line.indent_level <= nesting_indent { + if line.indent_length <= nesting_indent { if line.is_comment_only { self.class_status = Status::CommentAfter(nesting_indent); } else { @@ -548,7 +548,7 @@ impl BlankLinesChecker { } Status::CommentAfter(indent) => { if !line.is_comment_only { - if line.indent_level > indent { + if line.indent_length > indent { self.class_status = Status::Inside(indent); } self.class_status = Status::Outside; @@ -560,7 +560,7 @@ impl BlankLinesChecker { } if let Status::Inside(nesting_indent) = self.fn_status { - if line.indent_level <= nesting_indent { + if line.indent_length <= nesting_indent { if line.is_comment_only { self.fn_status = Status::CommentAfter(nesting_indent); } else { @@ -573,7 +573,7 @@ impl BlankLinesChecker { // we need to revert the variables. if !line.is_comment_only { if let Status::CommentAfter(indent) = self.fn_status { - if line.indent_level > indent { + if line.indent_length > indent { self.fn_status = Status::Inside(indent); } else { self.fn_status = Status::Outside; @@ -590,7 +590,7 @@ impl BlankLinesChecker { // The class/parent method's docstring can directly precede the def. && !matches!(self.follows, Follows::Docstring) // Do not trigger when the def follows an if/while/etc... - && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= line.indent_level) + && prev_indent_length.is_some_and(|prev_indent_length| prev_indent_length >= line.indent_length) // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !matches!(self.follows, Follows::Decorator) { @@ -615,7 +615,7 @@ impl BlankLinesChecker { // Allow groups of one-liners. && !(matches!(self.follows, Follows::Def) && !matches!(line.last_token, TokenKind::Colon)) // Only trigger on non-indented classes and functions (for example functions within an if are ignored) - && line.indent_level == 0 + && line.indent_length == 0 // Only apply to functions or classes. && is_top_level_token_or_decorator(line.first_token) { @@ -639,7 +639,7 @@ impl BlankLinesChecker { } if line.blank_lines > BLANK_LINES_TOP_LEVEL - || (line.indent_level > 0 && line.blank_lines > BLANK_LINES_METHOD_LEVEL) + || (line.indent_length > 0 && line.blank_lines > BLANK_LINES_METHOD_LEVEL) { // E303 let mut diagnostic = Diagnostic::new( @@ -649,7 +649,7 @@ impl BlankLinesChecker { line.first_token_range, ); - let chars_to_remove = if line.indent_level > 0 { + let chars_to_remove = if line.indent_length > 0 { u32::try_from(line.preceding_blank_characters) .expect("Number of blank characters to be small.") - BLANK_LINES_METHOD_LEVEL @@ -686,7 +686,7 @@ impl BlankLinesChecker { if line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL && is_top_level_token(self.previous_unindented_token) - && line.indent_level == 0 + && line.indent_length == 0 && !line.is_comment_only && !is_top_level_token_or_decorator(line.first_token) { @@ -719,7 +719,7 @@ impl BlankLinesChecker { // The class's docstring can directly precede the first function. && !matches!(self.follows, Follows::Docstring) // Do not trigger when the def/class follows an "indenting token" (if/while/etc...). - && prev_indent_level.is_some_and(|prev_indent_level| prev_indent_level >= line.indent_level) + && prev_indent_length.is_some_and(|prev_indent_length| prev_indent_length >= line.indent_length) // Allow groups of one-liners. && !(matches!(self.follows, Follows::Def) && line.last_token != TokenKind::Colon) { @@ -743,7 +743,7 @@ impl BlankLinesChecker { match line.first_token { TokenKind::Class => { if matches!(self.class_status, Status::Outside) { - self.class_status = Status::Inside(line.indent_level); + self.class_status = Status::Inside(line.indent_length); } self.follows = Follows::Other; } @@ -752,7 +752,7 @@ impl BlankLinesChecker { } TokenKind::Def => { if matches!(self.fn_status, Status::Outside) { - self.fn_status = Status::Inside(line.indent_level); + self.fn_status = Status::Inside(line.indent_length); } self.follows = Follows::Def; } @@ -771,7 +771,7 @@ impl BlankLinesChecker { self.last_non_comment_line_end = line.last_token_end; - if line.indent_level == 0 { + if line.indent_length == 0 { self.previous_unindented_token = Some(line.first_token); } } From 605470728ae7b2c9414845128399d66eed3c676d Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 13 Jan 2024 21:45:45 +0900 Subject: [PATCH 096/122] refactor: make current_blank_lines and current_blank_characters into local variables when building the logical line info See https://github.com/astral-sh/ruff/pull/9266#discussion_r1447027437 --- .../rules/pycodestyle/rules/blank_lines.rs | 80 ++++++++++--------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 8d77efa37e0e8..f6a7e230241db 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -343,10 +343,6 @@ struct LinePreprocessor<'a> { locator: &'a Locator<'a>, /// Number of previous consecutive blank lines. previous_blank_lines: u32, - /// Number of consecutive blank lines. - current_blank_lines: u32, - /// Number of blank characters in the blank lines (\n vs \r\n for example). - current_blank_characters: usize, } impl<'a> LinePreprocessor<'a> { @@ -355,8 +351,6 @@ impl<'a> LinePreprocessor<'a> { tokens: tokens.iter().flatten(), locator, previous_blank_lines: 0, - current_blank_lines: 0, - current_blank_characters: 0, } } } @@ -367,41 +361,58 @@ impl<'a> Iterator for LinePreprocessor<'a> { fn next(&mut self) -> Option { let mut line_is_comment_only = true; let mut is_docstring = true; + // Number of consecutive blank lines. + let mut current_blank_lines = 0u32; + // Number of blank characters in the blank lines (\n vs \r\n for example). + let mut current_blank_characters: usize = 0; let mut first_token: Option = None; let mut first_token_range: Option = None; let mut last_token: Option = None; let mut parens = 0u32; while let Some((token, range)) = self.tokens.next() { - let token = TokenKind::from_token(token); - if matches!(token, TokenKind::Indent | TokenKind::Dedent) { + let token_kind = TokenKind::from_token(token); + + if matches!(token_kind, TokenKind::Indent | TokenKind::Dedent) { continue; } - if token != TokenKind::Newline { - // Ideally, we would like to have a "async def" token since we care about the "def" part. - // As a work around, we ignore the first token if it is "async". - if first_token.is_none() && token != TokenKind::Async { - first_token = Some(token); - } - if first_token_range.is_none() { - first_token_range = Some(*range); - } + // At the start of the line... + if first_token.is_none() { + // An empty line + if token_kind == TokenKind::NonLogicalNewline { + current_blank_lines += 1; + current_blank_characters += range.len().to_usize(); + // self.current_blank_characters += + // range.end().to_usize() - first_range.start().to_usize() + 1; - if !token.is_trivia() { - line_is_comment_only = false; + continue; } - // A docstring line is composed only of the docstring (TokenKind::String) and trivia tokens. - // (If a comment follows a docstring, we still count the line as a docstring) - if token != TokenKind::String && !token.is_trivia() { - is_docstring = false; + if !token_kind.is_newline() { + // Ideally, we would like to have a "async def" token since we care about the "def" part. + // As a work around, we ignore the first token if it is "async". + if token_kind != TokenKind::Async { + first_token = Some(token_kind.clone()); + } + + if first_token_range.is_none() { + first_token_range = Some(*range); + } } + } + + if !token_kind.is_trivia() { + line_is_comment_only = false; + } - last_token = Some(token); + // A docstring line is composed only of the docstring (TokenKind::String) and trivia tokens. + // (If a comment follows a docstring, we still count the line as a docstring) + if token_kind != TokenKind::String && !token_kind.is_trivia() { + is_docstring = false; } - match token { + match token_kind { TokenKind::Lbrace | TokenKind::Lpar | TokenKind::Lsqb => { parens = parens.saturating_add(1); } @@ -426,16 +437,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { let indent_length = expand_indent(self.locator.slice(range)); - // Empty line - if first_token == Some(TokenKind::NonLogicalNewline) { - self.current_blank_lines += 1; - self.current_blank_characters += - range.end().to_usize() - first_range.start().to_usize() + 1; - return self.next(); - } - - if self.previous_blank_lines < self.current_blank_lines { - self.previous_blank_lines = self.current_blank_lines; + if self.previous_blank_lines < current_blank_lines { + self.previous_blank_lines = current_blank_lines; } let logical_line = LogicalLineInfo { @@ -448,20 +451,19 @@ impl<'a> Iterator for LinePreprocessor<'a> { is_comment_only: line_is_comment_only, is_docstring, indent_length, - blank_lines: self.current_blank_lines, + blank_lines: current_blank_lines, preceding_blank_lines: self.previous_blank_lines, - preceding_blank_characters: self.current_blank_characters, + preceding_blank_characters: current_blank_characters, }; if !line_is_comment_only { self.previous_blank_lines = 0; } - self.current_blank_lines = 0; - self.current_blank_characters = 0; return Some(logical_line); } _ => {} } + last_token = Some(token_kind); } None From 4c54d05a9fa18e63631778cd197c51abae33af05 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 13 Jan 2024 22:10:28 +0900 Subject: [PATCH 097/122] docs: add docstring to explain the difference between blank_lines and preceding_blank_lines --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index f6a7e230241db..46ba921f6cd21 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -331,7 +331,10 @@ struct LogicalLineInfo { is_comment_only: bool, is_docstring: bool, indent_length: usize, + /// `blank_lines` is the straightforward amount of blank lines preceding the current line. blank_lines: u32, + /// `preceding_blank_lines` is the maximum number of consecutive blank lines between the current line + /// and the previous non-comment logical line. preceding_blank_lines: u32, preceding_blank_characters: usize, } From c07ccdf9fae2aa1f2bdf28a9e0c97bd930aa9965 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 13 Jan 2024 22:22:54 +0900 Subject: [PATCH 098/122] docs: expand on the previous_blank_lines comment. --- .../ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 46ba921f6cd21..9a022ad17c4a5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -335,6 +335,8 @@ struct LogicalLineInfo { blank_lines: u32, /// `preceding_blank_lines` is the maximum number of consecutive blank lines between the current line /// and the previous non-comment logical line. + /// One of its main uses is to allow a comment to directly precede a class/function definition. + /// It is also used to match the results of pydocstyle. preceding_blank_lines: u32, preceding_blank_characters: usize, } @@ -344,7 +346,9 @@ struct LogicalLineInfo { struct LinePreprocessor<'a> { tokens: Flatten>>, locator: &'a Locator<'a>, - /// Number of previous consecutive blank lines. + /// Maximum number of consecutive blank lines between the current line and the previous non-comment logical line. + /// One of its main uses is to allow a comment to directly precede a class/function definition. + /// It is also used to match the results of pydocstyle. previous_blank_lines: u32, } From 5e342ac41b2ebe9c650ed151cf2abc7fe5458c3e Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 13 Jan 2024 22:26:18 +0900 Subject: [PATCH 099/122] refactor: previous_blank_lines -> preceding_blank_lines Done in order to not have two differents names for the same thing. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 9a022ad17c4a5..d3e6d4776b5f1 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -349,7 +349,7 @@ struct LinePreprocessor<'a> { /// Maximum number of consecutive blank lines between the current line and the previous non-comment logical line. /// One of its main uses is to allow a comment to directly precede a class/function definition. /// It is also used to match the results of pydocstyle. - previous_blank_lines: u32, + preceding_blank_lines: u32, } impl<'a> LinePreprocessor<'a> { @@ -357,7 +357,7 @@ impl<'a> LinePreprocessor<'a> { LinePreprocessor { tokens: tokens.iter().flatten(), locator, - previous_blank_lines: 0, + preceding_blank_lines: 0, } } } @@ -444,8 +444,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { let indent_length = expand_indent(self.locator.slice(range)); - if self.previous_blank_lines < current_blank_lines { - self.previous_blank_lines = current_blank_lines; + if self.preceding_blank_lines < current_blank_lines { + self.preceding_blank_lines = current_blank_lines; } let logical_line = LogicalLineInfo { @@ -459,12 +459,12 @@ impl<'a> Iterator for LinePreprocessor<'a> { is_docstring, indent_length, blank_lines: current_blank_lines, - preceding_blank_lines: self.previous_blank_lines, + preceding_blank_lines: self.preceding_blank_lines, preceding_blank_characters: current_blank_characters, }; if !line_is_comment_only { - self.previous_blank_lines = 0; + self.preceding_blank_lines = 0; } return Some(logical_line); } From 273a711a6618a114b0d9e73362737c062bed75ad Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Mon, 15 Jan 2024 19:18:49 +0900 Subject: [PATCH 100/122] Fix clippy errors. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 854ece749dc05..c02725ae815f4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -384,7 +384,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { let mut last_token: Option = None; let mut parens = 0u32; - while let Some((token, range)) = self.tokens.next() { + for (token, range) in self.tokens.by_ref() { let token_kind = TokenKind::from_token(token); if matches!(token_kind, TokenKind::Indent | TokenKind::Dedent) { @@ -407,7 +407,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { // Ideally, we would like to have a "async def" token since we care about the "def" part. // As a work around, we ignore the first token if it is "async". if token_kind != TokenKind::Async { - first_token = Some(token_kind.clone()); + first_token = Some(token_kind); } if first_token_range.is_none() { From 2fb48bc21281eea41d746ff32df34648e312d1d1 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 15 Jan 2024 12:19:51 +0100 Subject: [PATCH 101/122] Introduce `LogicalLineKind` --- .../rules/pycodestyle/rules/blank_lines.rs | 141 +++++++++++------- 1 file changed, 88 insertions(+), 53 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 854ece749dc05..c2fd3d5c40ee4 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -325,10 +325,14 @@ fn is_top_level_token_or_decorator(token: TokenKind) -> bool { #[derive(Debug)] struct LogicalLineInfo { - first_token: TokenKind, + kind: LogicalLineKind, first_token_range: TextRange, + + // The token's kind right before the newline ending the logical line. last_token: TokenKind, - last_token_end: TextSize, + + // The end of the logical line including the newline. + logical_line_end: TextSize, is_comment_only: bool, is_docstring: bool, indent_length: usize, @@ -345,7 +349,7 @@ struct LogicalLineInfo { /// Iterator that processes tokens until a full logical line (or comment line) is "built". /// It then returns characteristics of that logical line (see `LogicalLineInfo`). struct LinePreprocessor<'a> { - tokens: Flatten>>, + tokens: Iter<'a, Result<(Tok, TextRange), LexicalError>>, locator: &'a Locator<'a>, indent_width: IndentWidth, /// Maximum number of consecutive blank lines between the current line and the previous non-comment logical line. @@ -361,7 +365,7 @@ impl<'a> LinePreprocessor<'a> { indent_width: IndentWidth, ) -> LinePreprocessor<'a> { LinePreprocessor { - tokens: tokens.iter().flatten(), + tokens: tokens.iter(), locator, preceding_blank_lines: 0, indent_width, @@ -374,25 +378,33 @@ impl<'a> Iterator for LinePreprocessor<'a> { fn next(&mut self) -> Option { let mut line_is_comment_only = true; - let mut is_docstring = true; + let mut is_docstring = false; // Number of consecutive blank lines. let mut current_blank_lines = 0u32; // Number of blank characters in the blank lines (\n vs \r\n for example). let mut current_blank_characters: usize = 0; - let mut first_token: Option = None; - let mut first_token_range: Option = None; - let mut last_token: Option = None; + let mut logical_line_start: Option<(LogicalLineKind, TextRange)> = None; + let mut last_token: TokenKind = TokenKind::EndOfFile; let mut parens = 0u32; - while let Some((token, range)) = self.tokens.next() { - let token_kind = TokenKind::from_token(token); + while let Some(result) = self.tokens.next() { + let Ok((token, range)) = result else { + continue; + }; - if matches!(token_kind, TokenKind::Indent | TokenKind::Dedent) { + if matches!(token, Tok::Indent | Tok::Dedent) { continue; } + let token_kind = TokenKind::from_token(token); + + let (logical_line_kind, first_token_range) = if let Some(first_token_range) = + logical_line_start + { + first_token_range + } // At the start of the line... - if first_token.is_none() { + else { // An empty line if token_kind == TokenKind::NonLogicalNewline { current_blank_lines += 1; @@ -403,18 +415,25 @@ impl<'a> Iterator for LinePreprocessor<'a> { continue; } - if !token_kind.is_newline() { - // Ideally, we would like to have a "async def" token since we care about the "def" part. - // As a work around, we ignore the first token if it is "async". - if token_kind != TokenKind::Async { - first_token = Some(token_kind.clone()); + is_docstring = token_kind == TokenKind::String; + + let logical_line_kind = match token_kind { + TokenKind::Class => LogicalLineKind::Class, + TokenKind::Comment => LogicalLineKind::Comment, + TokenKind::At => LogicalLineKind::Decorator, + TokenKind::Def => LogicalLineKind::Function, + TokenKind::Async + if matches!(self.tokens.as_slice().first(), Some(Ok((Tok::Def, _)))) => + { + LogicalLineKind::Function } + _ => LogicalLineKind::Other, + }; - if first_token_range.is_none() { - first_token_range = Some(*range); - } - } - } + logical_line_start = Some((logical_line_kind, *range)); + + (logical_line_kind, *range) + }; if !token_kind.is_trivia() { line_is_comment_only = false; @@ -436,17 +455,9 @@ impl<'a> Iterator for LinePreprocessor<'a> { TokenKind::Newline | TokenKind::NonLogicalNewline if parens == 0 => { let last_token_end = range.end(); - // For a line to be a docstring, the first token must be a string (since indents are ignored) - if first_token != Some(TokenKind::String) { - is_docstring = false; - } - - let first_range = first_token_range - .expect("that Newline cannot be the first token of a line (there is at least a NonLogicalNewline token)"); - let range = TextRange::new( - self.locator.line_start(first_range.start()), - first_range.start(), + self.locator.line_start(first_token_range.start()), + first_token_range.start(), ); let indent_length = expand_indent(self.locator.slice(range), self.indent_width); @@ -456,12 +467,10 @@ impl<'a> Iterator for LinePreprocessor<'a> { } let logical_line = LogicalLineInfo { - first_token: first_token - .expect("that Newline cannot be the first token of a line (there is at least a NonLogicalNewline token)"), - first_token_range: first_range, - last_token: last_token - .expect("that Newline cannot be the first token of a line (there is at least a NonLogicalNewline token)"), - last_token_end, + kind: logical_line_kind, + first_token_range, + last_token, + logical_line_end: last_token_end, is_comment_only: line_is_comment_only, is_docstring, indent_length, @@ -477,7 +486,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { } _ => {} } - last_token = Some(token_kind); + + last_token = token_kind; } None @@ -514,7 +524,7 @@ pub(crate) struct BlankLinesChecker { /// Used for the fix in case a comment separates two non-comment logical lines to make the comment "stick" /// to the second line instead of the first. last_non_comment_line_end: TextSize, - previous_unindented_token: Option, + previous_unindented_line_kind: Option, } impl BlankLinesChecker { @@ -602,7 +612,7 @@ impl BlankLinesChecker { if self.is_not_first_logical_line { if line.preceding_blank_lines == 0 // Only applies to methods. - && line.first_token == TokenKind::Def + && matches!(line.kind, LogicalLineKind::Function) && matches!(self.class_status, Status::Inside(_)) // The class/parent method's docstring can directly precede the def. && !matches!(self.follows, Follows::Docstring) @@ -634,7 +644,7 @@ impl BlankLinesChecker { // Only trigger on non-indented classes and functions (for example functions within an if are ignored) && line.indent_length == 0 // Only apply to functions or classes. - && is_top_level_token_or_decorator(line.first_token) + && line.kind.is_top_level() { // E302 let mut diagnostic = Diagnostic::new( @@ -702,10 +712,12 @@ impl BlankLinesChecker { } if line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL - && is_top_level_token(self.previous_unindented_token) + && self + .previous_unindented_line_kind + .is_some_and(|kind| kind.is_top_level()) && line.indent_length == 0 && !line.is_comment_only - && !is_top_level_token_or_decorator(line.first_token) + && !line.kind.is_top_level() { // E305 let mut diagnostic = Diagnostic::new( @@ -730,7 +742,7 @@ impl BlankLinesChecker { if line.preceding_blank_lines == 0 // Only apply to nested functions. && matches!(self.fn_status, Status::Inside(_)) - && is_top_level_token_or_decorator(line.first_token) + && line.kind.is_top_level() // Allow following a decorator (if there is an error it will be triggered on the first decorator). && !matches!(self.follows, Follows::Decorator) // The class's docstring can directly precede the first function. @@ -757,24 +769,24 @@ impl BlankLinesChecker { } } - match line.first_token { - TokenKind::Class => { + match line.kind { + LogicalLineKind::Class => { if matches!(self.class_status, Status::Outside) { self.class_status = Status::Inside(line.indent_length); } self.follows = Follows::Other; } - TokenKind::At => { + LogicalLineKind::Decorator => { self.follows = Follows::Decorator; } - TokenKind::Def => { + LogicalLineKind::Function => { if matches!(self.fn_status, Status::Outside) { self.fn_status = Status::Inside(line.indent_length); } self.follows = Follows::Def; } - TokenKind::Comment => {} - _ => { + LogicalLineKind::Comment => {} + LogicalLineKind::Other => { self.follows = Follows::Other; } } @@ -786,11 +798,34 @@ impl BlankLinesChecker { if !line.is_comment_only { self.is_not_first_logical_line = true; - self.last_non_comment_line_end = line.last_token_end; + self.last_non_comment_line_end = line.logical_line_end; if line.indent_length == 0 { - self.previous_unindented_token = Some(line.first_token); + self.previous_unindented_line_kind = Some(line.kind); } } } } + +#[derive(Copy, Clone, Debug)] +enum LogicalLineKind { + /// The clause header of a class definition + Class, + /// A decorator + Decorator, + /// The clause header of a function + Function, + /// A comment only line + Comment, + /// Any other statement or clause header + Other, +} + +impl LogicalLineKind { + fn is_top_level(self) -> bool { + matches!( + self, + LogicalLineKind::Class | LogicalLineKind::Function | LogicalLineKind::Decorator + ) + } +} From cafe5ff83ba463d7b2a5fac4adcb67c5bac995a4 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 15 Jan 2024 12:35:41 +0100 Subject: [PATCH 102/122] Remove the need for `line_start` --- .../test/fixtures/pycodestyle/E30.py | 6 +++ .../rules/pycodestyle/rules/blank_lines.rs | 53 ++++++++----------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 8efe185efc26e..12dd113a7ee2b 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -748,3 +748,9 @@ def a(): async def b(): pass # end + + +class Test: + async + + def a(self): pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index c2fd3d5c40ee4..cb0d8e25e2f4f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -1,18 +1,16 @@ +use std::slice::Iter; + use ruff_diagnostics::AlwaysFixableViolation; use ruff_diagnostics::Diagnostic; use ruff_diagnostics::Edit; use ruff_diagnostics::Fix; -use ruff_python_parser::Tok; -use std::iter::Flatten; -use std::slice::Iter; - use ruff_macros::{derive_message_formats, violation}; use ruff_python_codegen::Stylist; use ruff_python_parser::lexer::LexResult; use ruff_python_parser::lexer::LexicalError; +use ruff_python_parser::Tok; use ruff_python_parser::TokenKind; use ruff_source_file::Locator; - use ruff_text_size::TextRange; use ruff_text_size::TextSize; @@ -312,17 +310,6 @@ impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { } } -/// Returns `true` if the token is a top level token. -/// It is sufficient to test for Class and Def since the `LinePreprocessor` ignores Async tokens. -fn is_top_level_token(token: Option) -> bool { - matches!(&token, Some(TokenKind::Class | TokenKind::Def)) -} - -/// Returns `true` if the token is At, Async, Class or Def -fn is_top_level_token_or_decorator(token: TokenKind) -> bool { - matches!(&token, TokenKind::Class | TokenKind::Def | TokenKind::At) -} - #[derive(Debug)] struct LogicalLineInfo { kind: LogicalLineKind, @@ -334,6 +321,8 @@ struct LogicalLineInfo { // The end of the logical line including the newline. logical_line_end: TextSize, is_comment_only: bool, + + /// `true` if the line is a string only (including trivia tokens) line, which is a docstring if coming right after a class/function definition. is_docstring: bool, indent_length: usize, /// `blank_lines` is the straightforward amount of blank lines preceding the current line. @@ -352,6 +341,8 @@ struct LinePreprocessor<'a> { tokens: Iter<'a, Result<(Tok, TextRange), LexicalError>>, locator: &'a Locator<'a>, indent_width: IndentWidth, + /// The start position of the next logical line. + line_start: TextSize, /// Maximum number of consecutive blank lines between the current line and the previous non-comment logical line. /// One of its main uses is to allow a comment to directly precede a class/function definition. /// It is also used to match the results of pydocstyle. @@ -367,6 +358,7 @@ impl<'a> LinePreprocessor<'a> { LinePreprocessor { tokens: tokens.iter(), locator, + line_start: TextSize::new(0), preceding_blank_lines: 0, indent_width, } @@ -409,8 +401,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { if token_kind == TokenKind::NonLogicalNewline { current_blank_lines += 1; current_blank_characters += range.len().to_usize(); - // self.current_blank_characters += - // range.end().to_usize() - first_range.start().to_usize() + 1; + + self.line_start = range.end(); continue; } @@ -453,24 +445,20 @@ impl<'a> Iterator for LinePreprocessor<'a> { parens = parens.saturating_sub(1); } TokenKind::Newline | TokenKind::NonLogicalNewline if parens == 0 => { - let last_token_end = range.end(); - - let range = TextRange::new( - self.locator.line_start(first_token_range.start()), - first_token_range.start(), - ); + let indent_range = TextRange::new(self.line_start, first_token_range.start()); - let indent_length = expand_indent(self.locator.slice(range), self.indent_width); + let indent_length = + expand_indent(self.locator.slice(indent_range), self.indent_width); - if self.preceding_blank_lines < current_blank_lines { - self.preceding_blank_lines = current_blank_lines; - } + // if self.preceding_blank_lines < current_blank_lines { + // self.preceding_blank_lines = current_blank_lines; + // } let logical_line = LogicalLineInfo { kind: logical_line_kind, first_token_range, last_token, - logical_line_end: last_token_end, + logical_line_end: range.end(), is_comment_only: line_is_comment_only, is_docstring, indent_length, @@ -482,6 +470,9 @@ impl<'a> Iterator for LinePreprocessor<'a> { if !line_is_comment_only { self.preceding_blank_lines = 0; } + // Set the start for the next logical line. + self.line_start = range.end(); + return Some(logical_line); } _ => {} @@ -714,7 +705,7 @@ impl BlankLinesChecker { if line.preceding_blank_lines < BLANK_LINES_TOP_LEVEL && self .previous_unindented_line_kind - .is_some_and(|kind| kind.is_top_level()) + .is_some_and(LogicalLineKind::is_top_level) && line.indent_length == 0 && !line.is_comment_only && !line.kind.is_top_level() @@ -740,7 +731,7 @@ impl BlankLinesChecker { } if line.preceding_blank_lines == 0 - // Only apply to nested functions. + // Only apply to nested functions. && matches!(self.fn_status, Status::Inside(_)) && line.kind.is_top_level() // Allow following a decorator (if there is an error it will be triggered on the first decorator). From e739a445c89e884b07da6ac3330948c7e63c0088 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 15 Jan 2024 12:45:35 +0100 Subject: [PATCH 103/122] Add a few new tests, remove unnecessary `as_str` and `to_string` calls --- .../test/fixtures/pycodestyle/E30.py | 18 + .../rules/pycodestyle/rules/blank_lines.rs | 70 ++-- ...ules__pycodestyle__tests__E303_E30.py.snap | 34 ++ ...ules__pycodestyle__tests__E304_E30.py.snap | 35 +- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 316 +++++++++--------- 6 files changed, 337 insertions(+), 262 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 12dd113a7ee2b..e7265a2ab18d2 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -606,6 +606,13 @@ def b(self): # E304 @decorator +def function(): + pass + + +@decorator + +# comment def function(): pass # end @@ -754,3 +761,14 @@ class Test: async def a(self): pass + + +class Test: + + + # comment + + + # another comment + + def test(self): pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index cb0d8e25e2f4f..bdbfa472ec91f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -320,19 +320,22 @@ struct LogicalLineInfo { // The end of the logical line including the newline. logical_line_end: TextSize, + + // `true` if this is not a blank but only consists of a comment. is_comment_only: bool, /// `true` if the line is a string only (including trivia tokens) line, which is a docstring if coming right after a class/function definition. is_docstring: bool, indent_length: usize, - /// `blank_lines` is the straightforward amount of blank lines preceding the current line. + + /// The number of blank lines preceding the current line. blank_lines: u32, - /// `preceding_blank_lines` is the maximum number of consecutive blank lines between the current line + blank_lines_len: TextSize, + + /// The maximum number of consecutive blank lines between the current line /// and the previous non-comment logical line. /// One of its main uses is to allow a comment to directly precede a class/function definition. - /// It is also used to match the results of pydocstyle. preceding_blank_lines: u32, - preceding_blank_characters: usize, } /// Iterator that processes tokens until a full logical line (or comment line) is "built". @@ -345,8 +348,7 @@ struct LinePreprocessor<'a> { line_start: TextSize, /// Maximum number of consecutive blank lines between the current line and the previous non-comment logical line. /// One of its main uses is to allow a comment to directly precede a class/function definition. - /// It is also used to match the results of pydocstyle. - preceding_blank_lines: u32, + max_preceding_blank_lines: u32, } impl<'a> LinePreprocessor<'a> { @@ -359,7 +361,7 @@ impl<'a> LinePreprocessor<'a> { tokens: tokens.iter(), locator, line_start: TextSize::new(0), - preceding_blank_lines: 0, + max_preceding_blank_lines: 0, indent_width, } } @@ -371,10 +373,9 @@ impl<'a> Iterator for LinePreprocessor<'a> { fn next(&mut self) -> Option { let mut line_is_comment_only = true; let mut is_docstring = false; - // Number of consecutive blank lines. - let mut current_blank_lines = 0u32; - // Number of blank characters in the blank lines (\n vs \r\n for example). - let mut current_blank_characters: usize = 0; + // Number of consecutive blank lines directly preceding this logical line. + let mut blank_lines = 0; + let mut blank_lines_len = TextSize::default(); let mut logical_line_start: Option<(LogicalLineKind, TextRange)> = None; let mut last_token: TokenKind = TokenKind::EndOfFile; let mut parens = 0u32; @@ -399,8 +400,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { else { // An empty line if token_kind == TokenKind::NonLogicalNewline { - current_blank_lines += 1; - current_blank_characters += range.len().to_usize(); + blank_lines += 1; + blank_lines_len += range.len(); self.line_start = range.end(); @@ -414,6 +415,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { TokenKind::Comment => LogicalLineKind::Comment, TokenKind::At => LogicalLineKind::Decorator, TokenKind::Def => LogicalLineKind::Function, + // Lookahead to distinguish `async def` from `async with`. TokenKind::Async if matches!(self.tokens.as_slice().first(), Some(Ok((Tok::Def, _)))) => { @@ -450,9 +452,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { let indent_length = expand_indent(self.locator.slice(indent_range), self.indent_width); - // if self.preceding_blank_lines < current_blank_lines { - // self.preceding_blank_lines = current_blank_lines; - // } + self.max_preceding_blank_lines = + self.max_preceding_blank_lines.max(blank_lines); let logical_line = LogicalLineInfo { kind: logical_line_kind, @@ -462,14 +463,18 @@ impl<'a> Iterator for LinePreprocessor<'a> { is_comment_only: line_is_comment_only, is_docstring, indent_length, - blank_lines: current_blank_lines, - preceding_blank_lines: self.preceding_blank_lines, - preceding_blank_characters: current_blank_characters, + + blank_lines, + blank_lines_len, + + preceding_blank_lines: self.max_preceding_blank_lines, }; + // Reset the blank lines after a non-comment only line. if !line_is_comment_only { - self.preceding_blank_lines = 0; + self.max_preceding_blank_lines = 0; } + // Set the start for the next logical line. self.line_start = range.end(); @@ -620,7 +625,7 @@ impl BlankLinesChecker { line.first_token_range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), + stylist.line_ending().to_string(), locator.line_start(self.last_non_comment_line_end), ))); @@ -647,8 +652,6 @@ impl BlankLinesChecker { diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist .line_ending() - .as_str() - .to_string() .repeat((BLANK_LINES_TOP_LEVEL - line.preceding_blank_lines) as usize), locator.line_start(self.last_non_comment_line_end), ))); @@ -668,13 +671,9 @@ impl BlankLinesChecker { ); let chars_to_remove = if line.indent_length > 0 { - u32::try_from(line.preceding_blank_characters) - .expect("Number of blank characters to be small.") - - BLANK_LINES_METHOD_LEVEL + u32::from(line.blank_lines_len) - BLANK_LINES_METHOD_LEVEL } else { - u32::try_from(line.preceding_blank_characters) - .expect("Number of blank characters to be small.") - - BLANK_LINES_TOP_LEVEL + u32::from(line.blank_lines_len) - BLANK_LINES_TOP_LEVEL }; let end = locator.line_start(line.first_token_range.start()); let start = end - TextSize::new(chars_to_remove); @@ -690,12 +689,7 @@ impl BlankLinesChecker { let range = line.first_token_range; diagnostic.set_fix(Fix::safe_edit(Edit::deletion( - locator.line_start(range.start()) - - TextSize::new( - line.preceding_blank_characters - .try_into() - .expect("Number of blank characters to be small."), - ), + locator.line_start(range.start()) - line.blank_lines_len, locator.line_start(range.start()), ))); @@ -721,8 +715,6 @@ impl BlankLinesChecker { diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist .line_ending() - .as_str() - .to_string() .repeat((BLANK_LINES_TOP_LEVEL - line.blank_lines) as usize), locator.line_start(line.first_token_range.start()), ))); @@ -746,13 +738,13 @@ impl BlankLinesChecker { // E306 let mut diagnostic = Diagnostic::new( BlankLinesBeforeNestedDefinition { - actual_blank_lines: line.blank_lines, + actual_blank_lines: 0, }, line.first_token_range, ); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist.line_ending().as_str().to_string(), + stylist.line_ending().to_string(), locator.line_start(line.first_token_range.start()), ))); diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 1c37309ec21e5..8e0b8abbc6bc8 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -160,4 +160,38 @@ E30.py:602:5: E303 [*] Too many blank lines (2) 603 602 | # end 604 603 | +E30.py:769:5: E303 [*] Too many blank lines (2) + | +769 | # comment + | ^^^^^^^^^ E303 + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +765 765 | +766 766 | class Test: +767 767 | +768 |- +769 768 | # comment +770 769 | +771 770 | + +E30.py:772:5: E303 [*] Too many blank lines (2) + | +772 | # another comment + | ^^^^^^^^^^^^^^^^^ E303 +773 | +774 | def test(self): pass + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +768 768 | +769 769 | # comment +770 770 | +771 |- +772 771 | # another comment +773 772 | +774 773 | def test(self): pass + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 08ce97ff5d4ce..919528bdf3d2d 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -8,7 +8,6 @@ E30.py:609:1: E304 [*] blank lines found after function decorator 609 | def function(): | ^^^ E304 610 | pass -611 | # end | = help: Remove extraneous blank line(s) @@ -19,6 +18,38 @@ E30.py:609:1: E304 [*] blank lines found after function decorator 608 |- 609 608 | def function(): 610 609 | pass -611 610 | # end +611 610 | + +E30.py:615:1: E304 [*] blank lines found after function decorator + | +613 | @decorator +614 | +615 | # comment + | ^^^^^^^^^ E304 +616 | def function(): +617 | pass + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +611 611 | +612 612 | +613 613 | @decorator +614 |- +615 614 | # comment +616 615 | def function(): +617 616 | pass + +E30.py:616:1: E304 [*] blank lines found after function decorator + | +615 | # comment +616 | def function(): + | ^^^ E304 +617 | pass +618 | # end + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 3a5e99c6fa487..4070bd9f0edaf 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:621:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:628:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -620 | # another comment -621 | fn() +627 | # another comment +628 | fn() | ^^ E305 -622 | # end +629 | # end | = help: Add missing blank line(s) ℹ Safe fix -618 618 | # comment -619 619 | -620 620 | # another comment - 621 |+ - 622 |+ -621 623 | fn() -622 624 | # end -623 625 | +625 625 | # comment +626 626 | +627 627 | # another comment + 628 |+ + 629 |+ +628 630 | fn() +629 631 | # end +630 632 | -E30.py:632:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:639:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -631 | # another comment -632 | a = 1 +638 | # another comment +639 | a = 1 | ^ E305 -633 | # end +640 | # end | = help: Add missing blank line(s) ℹ Safe fix -629 629 | # comment -630 630 | -631 631 | # another comment - 632 |+ - 633 |+ -632 634 | a = 1 -633 635 | # end -634 636 | +636 636 | # comment +637 637 | +638 638 | # another comment + 639 |+ + 640 |+ +639 641 | a = 1 +640 642 | # end +641 643 | -E30.py:644:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:651:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -642 | # another comment -643 | -644 | try: +649 | # another comment +650 | +651 | try: | ^^^ E305 -645 | fn() -646 | except Exception: +652 | fn() +653 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -641 641 | -642 642 | # another comment -643 643 | - 644 |+ -644 645 | try: -645 646 | fn() -646 647 | except Exception: +648 648 | +649 649 | # another comment +650 650 | + 651 |+ +651 652 | try: +652 653 | fn() +653 654 | except Exception: -E30.py:656:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:663:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -655 | # Two spaces before comments, too. -656 | if a(): +662 | # Two spaces before comments, too. +663 | if a(): | ^^ E305 -657 | a() -658 | # end +664 | a() +665 | # end | = help: Add missing blank line(s) ℹ Safe fix -653 653 | print -654 654 | -655 655 | # Two spaces before comments, too. - 656 |+ - 657 |+ -656 658 | if a(): -657 659 | a() -658 660 | # end +660 660 | print +661 661 | +662 662 | # Two spaces before comments, too. + 663 |+ + 664 |+ +663 665 | if a(): +664 666 | a() +665 667 | # end -E30.py:669:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:676:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -667 | blah, blah -668 | -669 | if __name__ == '__main__': +674 | blah, blah +675 | +676 | if __name__ == '__main__': | ^^ E305 -670 | main() -671 | # end +677 | main() +678 | # end | = help: Add missing blank line(s) ℹ Safe fix -666 666 | def main(): -667 667 | blah, blah -668 668 | - 669 |+ -669 670 | if __name__ == '__main__': -670 671 | main() -671 672 | # end +673 673 | def main(): +674 674 | blah, blah +675 675 | + 676 |+ +676 677 | if __name__ == '__main__': +677 678 | main() +678 679 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 46d28bc5acfbd..9ce6e79f2850b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,223 +1,223 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:677:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:684:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -675 | def a(): -676 | x = 1 -677 | def b(): +682 | def a(): +683 | x = 1 +684 | def b(): | ^^^ E306 -678 | pass -679 | # end +685 | pass +686 | # end | = help: Add missing blank line ℹ Safe fix -674 674 | # E306:3:5 -675 675 | def a(): -676 676 | x = 1 - 677 |+ -677 678 | def b(): -678 679 | pass -679 680 | # end - -E30.py:685:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -683 | async def a(): -684 | x = 1 -685 | def b(): +681 681 | # E306:3:5 +682 682 | def a(): +683 683 | x = 1 + 684 |+ +684 685 | def b(): +685 686 | pass +686 687 | # end + +E30.py:692:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +690 | async def a(): +691 | x = 1 +692 | def b(): | ^^^ E306 -686 | pass -687 | # end +693 | pass +694 | # end | = help: Add missing blank line ℹ Safe fix -682 682 | #: E306:3:5 -683 683 | async def a(): -684 684 | x = 1 - 685 |+ -685 686 | def b(): -686 687 | pass -687 688 | # end - -E30.py:693:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -691 | def a(): -692 | x = 2 -693 | def b(): +689 689 | #: E306:3:5 +690 690 | async def a(): +691 691 | x = 1 + 692 |+ +692 693 | def b(): +693 694 | pass +694 695 | # end + +E30.py:700:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +698 | def a(): +699 | x = 2 +700 | def b(): | ^^^ E306 -694 | x = 1 -695 | def c(): +701 | x = 1 +702 | def c(): | = help: Add missing blank line ℹ Safe fix -690 690 | #: E306:3:5 E306:5:9 -691 691 | def a(): -692 692 | x = 2 - 693 |+ -693 694 | def b(): -694 695 | x = 1 -695 696 | def c(): - -E30.py:695:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -693 | def b(): -694 | x = 1 -695 | def c(): +697 697 | #: E306:3:5 E306:5:9 +698 698 | def a(): +699 699 | x = 2 + 700 |+ +700 701 | def b(): +701 702 | x = 1 +702 703 | def c(): + +E30.py:702:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +700 | def b(): +701 | x = 1 +702 | def c(): | ^^^ E306 -696 | pass -697 | # end +703 | pass +704 | # end | = help: Add missing blank line ℹ Safe fix -692 692 | x = 2 -693 693 | def b(): -694 694 | x = 1 - 695 |+ -695 696 | def c(): -696 697 | pass -697 698 | # end - -E30.py:703:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -701 | def a(): -702 | x = 1 -703 | class C: +699 699 | x = 2 +700 700 | def b(): +701 701 | x = 1 + 702 |+ +702 703 | def c(): +703 704 | pass +704 705 | # end + +E30.py:710:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +708 | def a(): +709 | x = 1 +710 | class C: | ^^^^^ E306 -704 | pass -705 | x = 2 +711 | pass +712 | x = 2 | = help: Add missing blank line ℹ Safe fix -700 700 | # E306:3:5 E306:6:5 -701 701 | def a(): -702 702 | x = 1 - 703 |+ -703 704 | class C: -704 705 | pass -705 706 | x = 2 - -E30.py:706:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -704 | pass -705 | x = 2 -706 | def b(): +707 707 | # E306:3:5 E306:6:5 +708 708 | def a(): +709 709 | x = 1 + 710 |+ +710 711 | class C: +711 712 | pass +712 713 | x = 2 + +E30.py:713:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +711 | pass +712 | x = 2 +713 | def b(): | ^^^ E306 -707 | pass -708 | # end +714 | pass +715 | # end | = help: Add missing blank line ℹ Safe fix -703 703 | class C: -704 704 | pass -705 705 | x = 2 - 706 |+ -706 707 | def b(): -707 708 | pass -708 709 | # end +710 710 | class C: +711 711 | pass +712 712 | x = 2 + 713 |+ +713 714 | def b(): +714 715 | pass +715 716 | # end -E30.py:715:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:722:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -713 | def bar(): -714 | pass -715 | def baz(): pass +720 | def bar(): +721 | pass +722 | def baz(): pass | ^^^ E306 -716 | # end +723 | # end | = help: Add missing blank line ℹ Safe fix -712 712 | def foo(): -713 713 | def bar(): -714 714 | pass - 715 |+ -715 716 | def baz(): pass -716 717 | # end -717 718 | +719 719 | def foo(): +720 720 | def bar(): +721 721 | pass + 722 |+ +722 723 | def baz(): pass +723 724 | # end +724 725 | -E30.py:722:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:729:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -720 | def foo(): -721 | def bar(): pass -722 | def baz(): +727 | def foo(): +728 | def bar(): pass +729 | def baz(): | ^^^ E306 -723 | pass -724 | # end +730 | pass +731 | # end | = help: Add missing blank line ℹ Safe fix -719 719 | # E306:3:5 -720 720 | def foo(): -721 721 | def bar(): pass - 722 |+ -722 723 | def baz(): -723 724 | pass -724 725 | # end - -E30.py:730:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -728 | def a(): -729 | x = 2 -730 | @decorator +726 726 | # E306:3:5 +727 727 | def foo(): +728 728 | def bar(): pass + 729 |+ +729 730 | def baz(): +730 731 | pass +731 732 | # end + +E30.py:737:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +735 | def a(): +736 | x = 2 +737 | @decorator | ^ E306 -731 | def b(): -732 | pass +738 | def b(): +739 | pass | = help: Add missing blank line ℹ Safe fix -727 727 | # E306 -728 728 | def a(): -729 729 | x = 2 - 730 |+ -730 731 | @decorator -731 732 | def b(): -732 733 | pass - -E30.py:739:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -737 | def a(): -738 | x = 2 -739 | @decorator +734 734 | # E306 +735 735 | def a(): +736 736 | x = 2 + 737 |+ +737 738 | @decorator +738 739 | def b(): +739 740 | pass + +E30.py:746:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +744 | def a(): +745 | x = 2 +746 | @decorator | ^ E306 -740 | async def b(): -741 | pass +747 | async def b(): +748 | pass | = help: Add missing blank line ℹ Safe fix -736 736 | # E306 -737 737 | def a(): -738 738 | x = 2 - 739 |+ -739 740 | @decorator -740 741 | async def b(): -741 742 | pass - -E30.py:748:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -746 | def a(): -747 | x = 2 -748 | async def b(): +743 743 | # E306 +744 744 | def a(): +745 745 | x = 2 + 746 |+ +746 747 | @decorator +747 748 | async def b(): +748 749 | pass + +E30.py:755:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +753 | def a(): +754 | x = 2 +755 | async def b(): | ^^^^^ E306 -749 | pass -750 | # end +756 | pass +757 | # end | = help: Add missing blank line ℹ Safe fix -745 745 | # E306 -746 746 | def a(): -747 747 | x = 2 - 748 |+ -748 749 | async def b(): -749 750 | pass -750 751 | # end +752 752 | # E306 +753 753 | def a(): +754 754 | x = 2 + 755 |+ +755 756 | async def b(): +756 757 | pass +757 758 | # end From b244f22f8cf893470b1bb6e055b0c41ce0fad0b7 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 19 Jan 2024 23:13:40 +0900 Subject: [PATCH 104/122] Reorder tests. --- .../test/fixtures/pycodestyle/E30.py | 40 ++- ...ules__pycodestyle__tests__E301_E30.py.snap | 52 +-- ...ules__pycodestyle__tests__E302_E30.py.snap | 266 +++++++------- ...ules__pycodestyle__tests__E303_E30.py.snap | 236 ++++++------- ...ules__pycodestyle__tests__E304_E30.py.snap | 61 ++-- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 324 +++++++++--------- 7 files changed, 556 insertions(+), 549 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index e7265a2ab18d2..78e1f5b01f95a 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -417,6 +417,14 @@ async def function1(): # end +# no error +class Test: + async + + def a(self): pass +# end + + # E301 class Class(object): @@ -603,13 +611,28 @@ def b(self): # end +# E303 +class Test: + + + # comment + + + # another comment + + def test(self): pass +# end + + # E304 @decorator def function(): pass +# end +# E304 @decorator # comment @@ -755,20 +778,3 @@ def a(): async def b(): pass # end - - -class Test: - async - - def a(self): pass - - -class Test: - - - # comment - - - # another comment - - def test(self): pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index 8920d9e0b93c7..a286249e86784 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:425:5: E301 [*] Expected 1 blank line, found 0 +E30.py:433:5: E301 [*] Expected 1 blank line, found 0 | -423 | def func1(): -424 | pass -425 | def func2(): +431 | def func1(): +432 | pass +433 | def func2(): | ^^^ E301 -426 | pass -427 | # end +434 | pass +435 | # end | = help: Add missing blank line(s) ℹ Safe fix -422 422 | -423 423 | def func1(): -424 424 | pass - 425 |+ -425 426 | def func2(): -426 427 | pass -427 428 | # end +430 430 | +431 431 | def func1(): +432 432 | pass + 433 |+ +433 434 | def func2(): +434 435 | pass +435 436 | # end -E30.py:436:5: E301 [*] Expected 1 blank line, found 0 +E30.py:444:5: E301 [*] Expected 1 blank line, found 0 | -434 | pass -435 | # comment -436 | def fn2(): +442 | pass +443 | # comment +444 | def fn2(): | ^^^ E301 -437 | pass -438 | # end +445 | pass +446 | # end | = help: Add missing blank line(s) ℹ Safe fix -432 432 | -433 433 | def fn1(): -434 434 | pass - 435 |+ -435 436 | # comment -436 437 | def fn2(): -437 438 | pass +440 440 | +441 441 | def fn1(): +442 442 | pass + 443 |+ +443 444 | # comment +444 445 | def fn2(): +445 446 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index 39801181c2793..a3a1bb795b26b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,186 +1,186 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:443:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:451:1: E302 [*] Expected 2 blank lines, found 0 | -441 | # E302 -442 | """Main module.""" -443 | def fn(): +449 | # E302 +450 | """Main module.""" +451 | def fn(): | ^^^ E302 -444 | pass -445 | # end +452 | pass +453 | # end | = help: Add missing blank line(s) ℹ Safe fix -440 440 | -441 441 | # E302 -442 442 | """Main module.""" - 443 |+ - 444 |+ -443 445 | def fn(): -444 446 | pass -445 447 | # end - -E30.py:450:1: E302 [*] Expected 2 blank lines, found 0 - | -448 | # E302 -449 | import sys -450 | def get_sys_path(): - | ^^^ E302 -451 | return sys.path -452 | # end - | - = help: Add missing blank line(s) - -ℹ Safe fix -447 447 | -448 448 | # E302 -449 449 | import sys - 450 |+ +448 448 | +449 449 | # E302 +450 450 | """Main module.""" 451 |+ -450 452 | def get_sys_path(): -451 453 | return sys.path -452 454 | # end + 452 |+ +451 453 | def fn(): +452 454 | pass +453 455 | # end -E30.py:459:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:458:1: E302 [*] Expected 2 blank lines, found 0 | -457 | pass -458 | -459 | def b(): +456 | # E302 +457 | import sys +458 | def get_sys_path(): | ^^^ E302 -460 | pass -461 | # end +459 | return sys.path +460 | # end | = help: Add missing blank line(s) ℹ Safe fix -456 456 | def a(): -457 457 | pass -458 458 | +455 455 | +456 456 | # E302 +457 457 | import sys + 458 |+ 459 |+ -459 460 | def b(): -460 461 | pass -461 462 | # end +458 460 | def get_sys_path(): +459 461 | return sys.path +460 462 | # end -E30.py:470:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:467:1: E302 [*] Expected 2 blank lines, found 1 | -468 | # comment -469 | -470 | def b(): +465 | pass +466 | +467 | def b(): | ^^^ E302 -471 | pass -472 | # end +468 | pass +469 | # end | = help: Add missing blank line(s) ℹ Safe fix -465 465 | def a(): -466 466 | pass -467 467 | - 468 |+ -468 469 | # comment -469 470 | -470 471 | def b(): - -E30.py:479:1: E302 [*] Expected 2 blank lines, found 1 - | -477 | pass -478 | -479 | async def b(): - | ^^^^^ E302 -480 | pass -481 | # end +464 464 | def a(): +465 465 | pass +466 466 | + 467 |+ +467 468 | def b(): +468 469 | pass +469 470 | # end + +E30.py:478:1: E302 [*] Expected 2 blank lines, found 1 + | +476 | # comment +477 | +478 | def b(): + | ^^^ E302 +479 | pass +480 | # end | = help: Add missing blank line(s) ℹ Safe fix -476 476 | def a(): -477 477 | pass -478 478 | - 479 |+ -479 480 | async def b(): -480 481 | pass -481 482 | # end - -E30.py:488:1: E302 [*] Expected 2 blank lines, found 1 - | -486 | pass -487 | -488 | async def x(y: int = 1): +473 473 | def a(): +474 474 | pass +475 475 | + 476 |+ +476 477 | # comment +477 478 | +478 479 | def b(): + +E30.py:487:1: E302 [*] Expected 2 blank lines, found 1 + | +485 | pass +486 | +487 | async def b(): | ^^^^^ E302 -489 | pass -490 | # end +488 | pass +489 | # end | = help: Add missing blank line(s) ℹ Safe fix -485 485 | async def x(): -486 486 | pass -487 487 | - 488 |+ -488 489 | async def x(y: int = 1): -489 490 | pass -490 491 | # end - -E30.py:496:1: E302 [*] Expected 2 blank lines, found 0 - | -494 | def bar(): -495 | pass -496 | def baz(): pass - | ^^^ E302 -497 | # end +484 484 | def a(): +485 485 | pass +486 486 | + 487 |+ +487 488 | async def b(): +488 489 | pass +489 490 | # end + +E30.py:496:1: E302 [*] Expected 2 blank lines, found 1 + | +494 | pass +495 | +496 | async def x(y: int = 1): + | ^^^^^ E302 +497 | pass +498 | # end | = help: Add missing blank line(s) ℹ Safe fix -493 493 | # E302 -494 494 | def bar(): -495 495 | pass +493 493 | async def x(): +494 494 | pass +495 495 | 496 |+ - 497 |+ -496 498 | def baz(): pass -497 499 | # end -498 500 | +496 497 | async def x(y: int = 1): +497 498 | pass +498 499 | # end -E30.py:502:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:504:1: E302 [*] Expected 2 blank lines, found 0 | -500 | # E302 -501 | def bar(): pass -502 | def baz(): - | ^^^ E302 +502 | def bar(): 503 | pass -504 | # end +504 | def baz(): pass + | ^^^ E302 +505 | # end | = help: Add missing blank line(s) ℹ Safe fix -499 499 | -500 500 | # E302 -501 501 | def bar(): pass - 502 |+ - 503 |+ -502 504 | def baz(): -503 505 | pass -504 506 | # end - -E30.py:512:1: E302 [*] Expected 2 blank lines, found 1 - | -511 | # comment -512 | @decorator - | ^ E302 -513 | def g(): -514 | pass +501 501 | # E302 +502 502 | def bar(): +503 503 | pass + 504 |+ + 505 |+ +504 506 | def baz(): pass +505 507 | # end +506 508 | + +E30.py:510:1: E302 [*] Expected 2 blank lines, found 0 + | +508 | # E302 +509 | def bar(): pass +510 | def baz(): + | ^^^ E302 +511 | pass +512 | # end | = help: Add missing blank line(s) ℹ Safe fix -508 508 | def f(): -509 509 | pass -510 510 | +507 507 | +508 508 | # E302 +509 509 | def bar(): pass + 510 |+ 511 |+ -511 512 | # comment -512 513 | @decorator -513 514 | def g(): +510 512 | def baz(): +511 513 | pass +512 514 | # end + +E30.py:520:1: E302 [*] Expected 2 blank lines, found 1 + | +519 | # comment +520 | @decorator + | ^ E302 +521 | def g(): +522 | pass + | + = help: Add missing blank line(s) + +ℹ Safe fix +516 516 | def f(): +517 517 | pass +518 518 | + 519 |+ +519 520 | # comment +520 521 | @decorator +521 522 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 8e0b8abbc6bc8..8865e47e95ea0 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,197 +1,197 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:523:5: E303 [*] Too many blank lines (2) +E30.py:531:5: E303 [*] Too many blank lines (2) | -523 | # arbitrary comment +531 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -524 | -525 | def inner(): # E306 not expected +532 | +533 | def inner(): # E306 not expected | = help: Remove extraneous blank line(s) ℹ Safe fix -519 519 | def fn(): -520 520 | _ = None -521 521 | -522 |- -523 522 | # arbitrary comment -524 523 | -525 524 | def inner(): # E306 not expected +527 527 | def fn(): +528 528 | _ = None +529 529 | +530 |- +531 530 | # arbitrary comment +532 531 | +533 532 | def inner(): # E306 not expected -E30.py:535:5: E303 [*] Too many blank lines (2) +E30.py:543:5: E303 [*] Too many blank lines (2) | -535 | # arbitrary comment +543 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -536 | def inner(): # E306 not expected -537 | pass +544 | def inner(): # E306 not expected +545 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -531 531 | def fn(): -532 532 | _ = None -533 533 | -534 |- -535 534 | # arbitrary comment -536 535 | def inner(): # E306 not expected -537 536 | pass +539 539 | def fn(): +540 540 | _ = None +541 541 | +542 |- +543 542 | # arbitrary comment +544 543 | def inner(): # E306 not expected +545 544 | pass -E30.py:546:1: E303 [*] Too many blank lines (3) +E30.py:554:1: E303 [*] Too many blank lines (3) | -546 | print() +554 | print() | ^^^^^ E303 -547 | # end +555 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -542 542 | print() -543 543 | -544 544 | -545 |- -546 545 | print() -547 546 | # end -548 547 | +550 550 | print() +551 551 | +552 552 | +553 |- +554 553 | print() +555 554 | # end +556 555 | -E30.py:555:1: E303 [*] Too many blank lines (3) +E30.py:563:1: E303 [*] Too many blank lines (3) | -555 | # comment +563 | # comment | ^^^^^^^^^ E303 -556 | -557 | print() +564 | +565 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -551 551 | print() -552 552 | -553 553 | -554 |- -555 554 | # comment -556 555 | -557 556 | print() +559 559 | print() +560 560 | +561 561 | +562 |- +563 562 | # comment +564 563 | +565 564 | print() -E30.py:566:5: E303 [*] Too many blank lines (2) +E30.py:574:5: E303 [*] Too many blank lines (2) | -566 | # comment +574 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -562 562 | def a(): -563 563 | print() -564 564 | -565 |- -566 565 | # comment -567 566 | -568 567 | +570 570 | def a(): +571 571 | print() +572 572 | +573 |- +574 573 | # comment +575 574 | +576 575 | -E30.py:569:5: E303 [*] Too many blank lines (2) +E30.py:577:5: E303 [*] Too many blank lines (2) | -569 | # another comment +577 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -570 | -571 | print() +578 | +579 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -565 565 | -566 566 | # comment -567 567 | -568 |- -569 568 | # another comment -570 569 | -571 570 | print() - -E30.py:580:1: E303 [*] Too many blank lines (3) - | -580 | / """This class docstring comes on line 5. -581 | | It gives error E303: too many blank lines (3) -582 | | """ +573 573 | +574 574 | # comment +575 575 | +576 |- +577 576 | # another comment +578 577 | +579 578 | print() + +E30.py:588:1: E303 [*] Too many blank lines (3) + | +588 | / """This class docstring comes on line 5. +589 | | It gives error E303: too many blank lines (3) +590 | | """ | |___^ E303 -583 | # end +591 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -576 576 | #!python -577 577 | -578 578 | -579 |- -580 579 | """This class docstring comes on line 5. -581 580 | It gives error E303: too many blank lines (3) -582 581 | """ +584 584 | #!python +585 585 | +586 586 | +587 |- +588 587 | """This class docstring comes on line 5. +589 588 | It gives error E303: too many blank lines (3) +590 589 | """ -E30.py:592:5: E303 [*] Too many blank lines (2) +E30.py:600:5: E303 [*] Too many blank lines (2) | -592 | def b(self): +600 | def b(self): | ^^^ E303 -593 | pass -594 | # end +601 | pass +602 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -588 588 | def a(self): -589 589 | pass -590 590 | -591 |- -592 591 | def b(self): -593 592 | pass -594 593 | # end +596 596 | def a(self): +597 597 | pass +598 598 | +599 |- +600 599 | def b(self): +601 600 | pass +602 601 | # end -E30.py:602:5: E303 [*] Too many blank lines (2) +E30.py:610:5: E303 [*] Too many blank lines (2) | -602 | a = 2 +610 | a = 2 | ^ E303 -603 | # end +611 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -598 598 | if True: -599 599 | a = 1 -600 600 | -601 |- -602 601 | a = 2 -603 602 | # end -604 603 | +606 606 | if True: +607 607 | a = 1 +608 608 | +609 |- +610 609 | a = 2 +611 610 | # end +612 611 | -E30.py:769:5: E303 [*] Too many blank lines (2) +E30.py:618:5: E303 [*] Too many blank lines (2) | -769 | # comment +618 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -765 765 | -766 766 | class Test: -767 767 | -768 |- -769 768 | # comment -770 769 | -771 770 | +614 614 | # E303 +615 615 | class Test: +616 616 | +617 |- +618 617 | # comment +619 618 | +620 619 | -E30.py:772:5: E303 [*] Too many blank lines (2) +E30.py:621:5: E303 [*] Too many blank lines (2) | -772 | # another comment +621 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -773 | -774 | def test(self): pass +622 | +623 | def test(self): pass | = help: Remove extraneous blank line(s) ℹ Safe fix -768 768 | -769 769 | # comment -770 770 | -771 |- -772 771 | # another comment -773 772 | -774 773 | def test(self): pass +617 617 | +618 618 | # comment +619 619 | +620 |- +621 620 | # another comment +622 621 | +623 622 | def test(self): pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 919528bdf3d2d..5c8c5c67821ed 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,52 +1,53 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:609:1: E304 [*] blank lines found after function decorator +E30.py:630:1: E304 [*] blank lines found after function decorator | -607 | @decorator -608 | -609 | def function(): +628 | @decorator +629 | +630 | def function(): | ^^^ E304 -610 | pass +631 | pass +632 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -605 605 | -606 606 | # E304 -607 607 | @decorator -608 |- -609 608 | def function(): -610 609 | pass -611 610 | +626 626 | +627 627 | # E304 +628 628 | @decorator +629 |- +630 629 | def function(): +631 630 | pass +632 631 | # end -E30.py:615:1: E304 [*] blank lines found after function decorator +E30.py:638:1: E304 [*] blank lines found after function decorator | -613 | @decorator -614 | -615 | # comment +636 | @decorator +637 | +638 | # comment | ^^^^^^^^^ E304 -616 | def function(): -617 | pass +639 | def function(): +640 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -611 611 | -612 612 | -613 613 | @decorator -614 |- -615 614 | # comment -616 615 | def function(): -617 616 | pass +634 634 | +635 635 | # E304 +636 636 | @decorator +637 |- +638 637 | # comment +639 638 | def function(): +640 639 | pass -E30.py:616:1: E304 [*] blank lines found after function decorator +E30.py:639:1: E304 [*] blank lines found after function decorator | -615 | # comment -616 | def function(): +638 | # comment +639 | def function(): | ^^^ E304 -617 | pass -618 | # end +640 | pass +641 | # end | = help: Remove extraneous blank line(s) diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 4070bd9f0edaf..d7c37ca642ebf 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:628:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:651:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -627 | # another comment -628 | fn() +650 | # another comment +651 | fn() | ^^ E305 -629 | # end +652 | # end | = help: Add missing blank line(s) ℹ Safe fix -625 625 | # comment -626 626 | -627 627 | # another comment - 628 |+ - 629 |+ -628 630 | fn() -629 631 | # end -630 632 | +648 648 | # comment +649 649 | +650 650 | # another comment + 651 |+ + 652 |+ +651 653 | fn() +652 654 | # end +653 655 | -E30.py:639:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:662:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -638 | # another comment -639 | a = 1 +661 | # another comment +662 | a = 1 | ^ E305 -640 | # end +663 | # end | = help: Add missing blank line(s) ℹ Safe fix -636 636 | # comment -637 637 | -638 638 | # another comment - 639 |+ - 640 |+ -639 641 | a = 1 -640 642 | # end -641 643 | +659 659 | # comment +660 660 | +661 661 | # another comment + 662 |+ + 663 |+ +662 664 | a = 1 +663 665 | # end +664 666 | -E30.py:651:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:674:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -649 | # another comment -650 | -651 | try: +672 | # another comment +673 | +674 | try: | ^^^ E305 -652 | fn() -653 | except Exception: +675 | fn() +676 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -648 648 | -649 649 | # another comment -650 650 | - 651 |+ -651 652 | try: -652 653 | fn() -653 654 | except Exception: +671 671 | +672 672 | # another comment +673 673 | + 674 |+ +674 675 | try: +675 676 | fn() +676 677 | except Exception: -E30.py:663:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:686:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -662 | # Two spaces before comments, too. -663 | if a(): +685 | # Two spaces before comments, too. +686 | if a(): | ^^ E305 -664 | a() -665 | # end +687 | a() +688 | # end | = help: Add missing blank line(s) ℹ Safe fix -660 660 | print -661 661 | -662 662 | # Two spaces before comments, too. - 663 |+ - 664 |+ -663 665 | if a(): -664 666 | a() -665 667 | # end +683 683 | print +684 684 | +685 685 | # Two spaces before comments, too. + 686 |+ + 687 |+ +686 688 | if a(): +687 689 | a() +688 690 | # end -E30.py:676:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:699:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -674 | blah, blah -675 | -676 | if __name__ == '__main__': +697 | blah, blah +698 | +699 | if __name__ == '__main__': | ^^ E305 -677 | main() -678 | # end +700 | main() +701 | # end | = help: Add missing blank line(s) ℹ Safe fix -673 673 | def main(): -674 674 | blah, blah -675 675 | - 676 |+ -676 677 | if __name__ == '__main__': -677 678 | main() -678 679 | # end +696 696 | def main(): +697 697 | blah, blah +698 698 | + 699 |+ +699 700 | if __name__ == '__main__': +700 701 | main() +701 702 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 9ce6e79f2850b..e554f1dd0b454 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,223 +1,223 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:684:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:707:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -682 | def a(): -683 | x = 1 -684 | def b(): +705 | def a(): +706 | x = 1 +707 | def b(): | ^^^ E306 -685 | pass -686 | # end +708 | pass +709 | # end | = help: Add missing blank line ℹ Safe fix -681 681 | # E306:3:5 -682 682 | def a(): -683 683 | x = 1 - 684 |+ -684 685 | def b(): -685 686 | pass -686 687 | # end - -E30.py:692:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -690 | async def a(): -691 | x = 1 -692 | def b(): +704 704 | # E306:3:5 +705 705 | def a(): +706 706 | x = 1 + 707 |+ +707 708 | def b(): +708 709 | pass +709 710 | # end + +E30.py:715:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +713 | async def a(): +714 | x = 1 +715 | def b(): | ^^^ E306 -693 | pass -694 | # end +716 | pass +717 | # end | = help: Add missing blank line ℹ Safe fix -689 689 | #: E306:3:5 -690 690 | async def a(): -691 691 | x = 1 - 692 |+ -692 693 | def b(): -693 694 | pass -694 695 | # end - -E30.py:700:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -698 | def a(): -699 | x = 2 -700 | def b(): +712 712 | #: E306:3:5 +713 713 | async def a(): +714 714 | x = 1 + 715 |+ +715 716 | def b(): +716 717 | pass +717 718 | # end + +E30.py:723:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +721 | def a(): +722 | x = 2 +723 | def b(): | ^^^ E306 -701 | x = 1 -702 | def c(): +724 | x = 1 +725 | def c(): | = help: Add missing blank line ℹ Safe fix -697 697 | #: E306:3:5 E306:5:9 -698 698 | def a(): -699 699 | x = 2 - 700 |+ -700 701 | def b(): -701 702 | x = 1 -702 703 | def c(): - -E30.py:702:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -700 | def b(): -701 | x = 1 -702 | def c(): +720 720 | #: E306:3:5 E306:5:9 +721 721 | def a(): +722 722 | x = 2 + 723 |+ +723 724 | def b(): +724 725 | x = 1 +725 726 | def c(): + +E30.py:725:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +723 | def b(): +724 | x = 1 +725 | def c(): | ^^^ E306 -703 | pass -704 | # end +726 | pass +727 | # end | = help: Add missing blank line ℹ Safe fix -699 699 | x = 2 -700 700 | def b(): -701 701 | x = 1 - 702 |+ -702 703 | def c(): -703 704 | pass -704 705 | # end - -E30.py:710:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -708 | def a(): -709 | x = 1 -710 | class C: +722 722 | x = 2 +723 723 | def b(): +724 724 | x = 1 + 725 |+ +725 726 | def c(): +726 727 | pass +727 728 | # end + +E30.py:733:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +731 | def a(): +732 | x = 1 +733 | class C: | ^^^^^ E306 -711 | pass -712 | x = 2 +734 | pass +735 | x = 2 | = help: Add missing blank line ℹ Safe fix -707 707 | # E306:3:5 E306:6:5 -708 708 | def a(): -709 709 | x = 1 - 710 |+ -710 711 | class C: -711 712 | pass -712 713 | x = 2 - -E30.py:713:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -711 | pass -712 | x = 2 -713 | def b(): +730 730 | # E306:3:5 E306:6:5 +731 731 | def a(): +732 732 | x = 1 + 733 |+ +733 734 | class C: +734 735 | pass +735 736 | x = 2 + +E30.py:736:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +734 | pass +735 | x = 2 +736 | def b(): | ^^^ E306 -714 | pass -715 | # end +737 | pass +738 | # end | = help: Add missing blank line ℹ Safe fix -710 710 | class C: -711 711 | pass -712 712 | x = 2 - 713 |+ -713 714 | def b(): -714 715 | pass -715 716 | # end - -E30.py:722:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -720 | def bar(): -721 | pass -722 | def baz(): pass +733 733 | class C: +734 734 | pass +735 735 | x = 2 + 736 |+ +736 737 | def b(): +737 738 | pass +738 739 | # end + +E30.py:745:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +743 | def bar(): +744 | pass +745 | def baz(): pass | ^^^ E306 -723 | # end +746 | # end | = help: Add missing blank line ℹ Safe fix -719 719 | def foo(): -720 720 | def bar(): -721 721 | pass - 722 |+ -722 723 | def baz(): pass -723 724 | # end -724 725 | - -E30.py:729:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -727 | def foo(): -728 | def bar(): pass -729 | def baz(): +742 742 | def foo(): +743 743 | def bar(): +744 744 | pass + 745 |+ +745 746 | def baz(): pass +746 747 | # end +747 748 | + +E30.py:752:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +750 | def foo(): +751 | def bar(): pass +752 | def baz(): | ^^^ E306 -730 | pass -731 | # end +753 | pass +754 | # end | = help: Add missing blank line ℹ Safe fix -726 726 | # E306:3:5 -727 727 | def foo(): -728 728 | def bar(): pass - 729 |+ -729 730 | def baz(): -730 731 | pass -731 732 | # end - -E30.py:737:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -735 | def a(): -736 | x = 2 -737 | @decorator +749 749 | # E306:3:5 +750 750 | def foo(): +751 751 | def bar(): pass + 752 |+ +752 753 | def baz(): +753 754 | pass +754 755 | # end + +E30.py:760:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +758 | def a(): +759 | x = 2 +760 | @decorator | ^ E306 -738 | def b(): -739 | pass +761 | def b(): +762 | pass | = help: Add missing blank line ℹ Safe fix -734 734 | # E306 -735 735 | def a(): -736 736 | x = 2 - 737 |+ -737 738 | @decorator -738 739 | def b(): -739 740 | pass - -E30.py:746:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -744 | def a(): -745 | x = 2 -746 | @decorator +757 757 | # E306 +758 758 | def a(): +759 759 | x = 2 + 760 |+ +760 761 | @decorator +761 762 | def b(): +762 763 | pass + +E30.py:769:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +767 | def a(): +768 | x = 2 +769 | @decorator | ^ E306 -747 | async def b(): -748 | pass +770 | async def b(): +771 | pass | = help: Add missing blank line ℹ Safe fix -743 743 | # E306 -744 744 | def a(): -745 745 | x = 2 - 746 |+ -746 747 | @decorator -747 748 | async def b(): -748 749 | pass - -E30.py:755:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -753 | def a(): -754 | x = 2 -755 | async def b(): +766 766 | # E306 +767 767 | def a(): +768 768 | x = 2 + 769 |+ +769 770 | @decorator +770 771 | async def b(): +771 772 | pass + +E30.py:778:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +776 | def a(): +777 | x = 2 +778 | async def b(): | ^^^^^ E306 -756 | pass -757 | # end +779 | pass +780 | # end | = help: Add missing blank line ℹ Safe fix -752 752 | # E306 -753 753 | def a(): -754 754 | x = 2 - 755 |+ -755 756 | async def b(): -756 757 | pass -757 758 | # end +775 775 | # E306 +776 776 | def a(): +777 777 | x = 2 + 778 |+ +778 779 | async def b(): +779 780 | pass +780 781 | # end From f8149908f36e9b1d9140c4a7d748b5893e686753 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Fri, 19 Jan 2024 23:32:52 +0900 Subject: [PATCH 105/122] Add comments to the text fixture. --- .../ruff_linter/resources/test/fixtures/pycodestyle/E30.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 78e1f5b01f95a..1b8b195ed0856 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -530,7 +530,7 @@ def fn(): # arbitrary comment - def inner(): # E306 not expected + def inner(): # E306 not expected (pycodestyle detects E306) pass # end @@ -541,7 +541,7 @@ def fn(): # arbitrary comment - def inner(): # E306 not expected + def inner(): # E306 not expected (pycodestyle detects E306) pass # end @@ -635,7 +635,7 @@ def function(): # E304 @decorator -# comment +# comment E304 not expected def function(): pass # end From 2c08381d925e068f69d8e2bf838fee99a1bbeeed Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 20 Jan 2024 12:50:06 +0900 Subject: [PATCH 106/122] Use BlankLines enum and keep track of the blank lines range. --- .../rules/pycodestyle/rules/blank_lines.rs | 139 ++++++++++++++---- ...ules__pycodestyle__tests__E302_E30.py.snap | 17 ++- ...ules__pycodestyle__tests__E303_E30.py.snap | 8 +- ...ules__pycodestyle__tests__E304_E30.py.snap | 8 +- 4 files changed, 130 insertions(+), 42 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index bdbfa472ec91f..91c84a2de299e 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -1,3 +1,4 @@ +use std::num::NonZeroU32; use std::slice::Iter; use ruff_diagnostics::AlwaysFixableViolation; @@ -329,13 +330,13 @@ struct LogicalLineInfo { indent_length: usize, /// The number of blank lines preceding the current line. - blank_lines: u32, + blank_lines: BlankLines, blank_lines_len: TextSize, /// The maximum number of consecutive blank lines between the current line /// and the previous non-comment logical line. /// One of its main uses is to allow a comment to directly precede a class/function definition. - preceding_blank_lines: u32, + preceding_blank_lines: BlankLines, } /// Iterator that processes tokens until a full logical line (or comment line) is "built". @@ -348,7 +349,7 @@ struct LinePreprocessor<'a> { line_start: TextSize, /// Maximum number of consecutive blank lines between the current line and the previous non-comment logical line. /// One of its main uses is to allow a comment to directly precede a class/function definition. - max_preceding_blank_lines: u32, + max_preceding_blank_lines: BlankLines, } impl<'a> LinePreprocessor<'a> { @@ -361,7 +362,7 @@ impl<'a> LinePreprocessor<'a> { tokens: tokens.iter(), locator, line_start: TextSize::new(0), - max_preceding_blank_lines: 0, + max_preceding_blank_lines: BlankLines::Zero, indent_width, } } @@ -374,7 +375,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { let mut line_is_comment_only = true; let mut is_docstring = false; // Number of consecutive blank lines directly preceding this logical line. - let mut blank_lines = 0; + let mut blank_lines = BlankLines::Zero; let mut blank_lines_len = TextSize::default(); let mut logical_line_start: Option<(LogicalLineKind, TextRange)> = None; let mut last_token: TokenKind = TokenKind::EndOfFile; @@ -400,7 +401,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { else { // An empty line if token_kind == TokenKind::NonLogicalNewline { - blank_lines += 1; + blank_lines.add(*range); blank_lines_len += range.len(); self.line_start = range.end(); @@ -452,8 +453,9 @@ impl<'a> Iterator for LinePreprocessor<'a> { let indent_length = expand_indent(self.locator.slice(indent_range), self.indent_width); - self.max_preceding_blank_lines = - self.max_preceding_blank_lines.max(blank_lines); + if blank_lines.count() > self.max_preceding_blank_lines.count() { + self.max_preceding_blank_lines = blank_lines; + } let logical_line = LogicalLineInfo { kind: logical_line_kind, @@ -463,7 +465,6 @@ impl<'a> Iterator for LinePreprocessor<'a> { is_comment_only: line_is_comment_only, is_docstring, indent_length, - blank_lines, blank_lines_len, @@ -472,7 +473,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { // Reset the blank lines after a non-comment only line. if !line_is_comment_only { - self.max_preceding_blank_lines = 0; + self.max_preceding_blank_lines = BlankLines::Zero; } // Set the start for the next logical line. @@ -490,6 +491,58 @@ impl<'a> Iterator for LinePreprocessor<'a> { } } +#[derive(Clone, Copy, Debug, Default)] +enum BlankLines { + /// No blank lines + #[default] + Zero, + + /// One or more blank lines + Many { count: NonZeroU32, range: TextRange }, +} + +impl BlankLines { + fn add(&mut self, line_range: TextRange) { + match self { + BlankLines::Zero => { + *self = BlankLines::Many { + count: NonZeroU32::MIN, + range: line_range, + } + } + BlankLines::Many { count, range } => { + assert_eq!(range.end(), line_range.start()); + *count = count.saturating_add(1); + *range = TextRange::new(range.start(), line_range.end()); + } + } + } + + fn count(&self) -> u32 { + match self { + BlankLines::Zero => 0, + BlankLines::Many { count, .. } => count.get(), + } + } +} + +use std::cmp::Ordering; + +impl PartialEq for BlankLines { + fn eq(&self, other: &u32) -> bool { + match self { + BlankLines::Zero => *other == 0, + BlankLines::Many { count, range: _ } => count.get() == *other, + } + } +} + +impl PartialOrd for BlankLines { + fn partial_cmp(&self, other: &u32) -> Option { + self.count().partial_cmp(other) + } +} + #[derive(Copy, Clone, Debug, Default)] enum Follows { #[default] @@ -620,7 +673,7 @@ impl BlankLinesChecker { // E301 let mut diagnostic = Diagnostic::new( BlankLineBetweenMethods { - actual_blank_lines: line.preceding_blank_lines, + actual_blank_lines: line.preceding_blank_lines.count(), }, line.first_token_range, ); @@ -645,16 +698,30 @@ impl BlankLinesChecker { // E302 let mut diagnostic = Diagnostic::new( BlankLinesTopLevel { - actual_blank_lines: line.preceding_blank_lines, + actual_blank_lines: line.preceding_blank_lines.count(), }, line.first_token_range, ); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .repeat((BLANK_LINES_TOP_LEVEL - line.preceding_blank_lines) as usize), - locator.line_start(self.last_non_comment_line_end), - ))); + + match line.blank_lines { + BlankLines::Many { + count: _blank_lines, + range: blank_lines_range, + } => { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + stylist + .line_ending() + .repeat((BLANK_LINES_TOP_LEVEL) as usize), + blank_lines_range, + ))); + } + BlankLines::Zero => diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .repeat((BLANK_LINES_TOP_LEVEL) as usize), + locator.line_start(self.last_non_comment_line_end), + ))), + } diagnostics.push(diagnostic); } @@ -665,7 +732,7 @@ impl BlankLinesChecker { // E303 let mut diagnostic = Diagnostic::new( TooManyBlankLines { - actual_blank_lines: line.blank_lines, + actual_blank_lines: line.blank_lines.count(), }, line.first_token_range, ); @@ -707,17 +774,37 @@ impl BlankLinesChecker { // E305 let mut diagnostic = Diagnostic::new( BlankLinesAfterFunctionOrClass { - actual_blank_lines: line.blank_lines, + actual_blank_lines: line.blank_lines.count(), }, line.first_token_range, ); - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .repeat((BLANK_LINES_TOP_LEVEL - line.blank_lines) as usize), - locator.line_start(line.first_token_range.start()), - ))); + // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + // stylist + // .line_ending() + // .repeat((BLANK_LINES_TOP_LEVEL - line.blank_lines) as usize), + // locator.line_start(line.first_token_range.start()), + // ))); + + match line.blank_lines { + BlankLines::Many { + count: _blank_lines, + range: blank_lines_range, + } => { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + stylist + .line_ending() + .repeat((BLANK_LINES_TOP_LEVEL) as usize), + blank_lines_range, + ))); + } + BlankLines::Zero => diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .repeat((BLANK_LINES_TOP_LEVEL) as usize), + locator.line_start(line.first_token_range.start()), + ))), + } diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index a3a1bb795b26b..a6a4fb871af5f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -75,13 +75,13 @@ E30.py:478:1: E302 [*] Expected 2 blank lines, found 1 = help: Add missing blank line(s) ℹ Safe fix -473 473 | def a(): -474 474 | pass 475 475 | - 476 |+ -476 477 | # comment -477 478 | +476 476 | # comment +477 477 | + 478 |+ 478 479 | def b(): +479 480 | pass +480 481 | # end E30.py:487:1: E302 [*] Expected 2 blank lines, found 1 | @@ -179,8 +179,9 @@ E30.py:520:1: E302 [*] Expected 2 blank lines, found 1 517 517 | pass 518 518 | 519 |+ -519 520 | # comment -520 521 | @decorator -521 522 | def g(): + 520 |+ +519 521 | # comment +520 522 | @decorator +521 523 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 8865e47e95ea0..58beadf153d8c 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -6,7 +6,7 @@ E30.py:531:5: E303 [*] Too many blank lines (2) 531 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 532 | -533 | def inner(): # E306 not expected +533 | def inner(): # E306 not expected (pycodestyle detects E306) | = help: Remove extraneous blank line(s) @@ -17,13 +17,13 @@ E30.py:531:5: E303 [*] Too many blank lines (2) 530 |- 531 530 | # arbitrary comment 532 531 | -533 532 | def inner(): # E306 not expected +533 532 | def inner(): # E306 not expected (pycodestyle detects E306) E30.py:543:5: E303 [*] Too many blank lines (2) | 543 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -544 | def inner(): # E306 not expected +544 | def inner(): # E306 not expected (pycodestyle detects E306) 545 | pass | = help: Remove extraneous blank line(s) @@ -34,7 +34,7 @@ E30.py:543:5: E303 [*] Too many blank lines (2) 541 541 | 542 |- 543 542 | # arbitrary comment -544 543 | def inner(): # E306 not expected +544 543 | def inner(): # E306 not expected (pycodestyle detects E306) 545 544 | pass E30.py:554:1: E303 [*] Too many blank lines (3) diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 5c8c5c67821ed..6a336c844b5f1 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -25,8 +25,8 @@ E30.py:638:1: E304 [*] blank lines found after function decorator | 636 | @decorator 637 | -638 | # comment - | ^^^^^^^^^ E304 +638 | # comment E304 not expected + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E304 639 | def function(): 640 | pass | @@ -37,13 +37,13 @@ E30.py:638:1: E304 [*] blank lines found after function decorator 635 635 | # E304 636 636 | @decorator 637 |- -638 637 | # comment +638 637 | # comment E304 not expected 639 638 | def function(): 640 639 | pass E30.py:639:1: E304 [*] blank lines found after function decorator | -638 | # comment +638 | # comment E304 not expected 639 | def function(): | ^^^ E304 640 | pass From fddc74013eb365ca81945118449c22a17bd52704 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 20 Jan 2024 13:17:02 +0900 Subject: [PATCH 107/122] Remove line.blank_lines_len and use BlankLines enum instead. Do range replacement instead of using blank_lines_len + deletion in order to avoid unsafe edits on Windows. --- .../rules/pycodestyle/rules/blank_lines.rs | 72 ++++++++++--------- ...ules__pycodestyle__tests__E304_E30.py.snap | 19 +---- 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 91c84a2de299e..1400bc60214af 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -331,7 +331,6 @@ struct LogicalLineInfo { /// The number of blank lines preceding the current line. blank_lines: BlankLines, - blank_lines_len: TextSize, /// The maximum number of consecutive blank lines between the current line /// and the previous non-comment logical line. @@ -376,7 +375,6 @@ impl<'a> Iterator for LinePreprocessor<'a> { let mut is_docstring = false; // Number of consecutive blank lines directly preceding this logical line. let mut blank_lines = BlankLines::Zero; - let mut blank_lines_len = TextSize::default(); let mut logical_line_start: Option<(LogicalLineKind, TextRange)> = None; let mut last_token: TokenKind = TokenKind::EndOfFile; let mut parens = 0u32; @@ -402,7 +400,6 @@ impl<'a> Iterator for LinePreprocessor<'a> { // An empty line if token_kind == TokenKind::NonLogicalNewline { blank_lines.add(*range); - blank_lines_len += range.len(); self.line_start = range.end(); @@ -466,8 +463,6 @@ impl<'a> Iterator for LinePreprocessor<'a> { is_docstring, indent_length, blank_lines, - blank_lines_len, - preceding_blank_lines: self.max_preceding_blank_lines, }; @@ -715,12 +710,14 @@ impl BlankLinesChecker { blank_lines_range, ))); } - BlankLines::Zero => diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .repeat((BLANK_LINES_TOP_LEVEL) as usize), - locator.line_start(self.last_non_comment_line_end), - ))), + BlankLines::Zero => { + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist + .line_ending() + .repeat((BLANK_LINES_TOP_LEVEL) as usize), + locator.line_start(self.last_non_comment_line_end), + ))); + } } diagnostics.push(diagnostic); @@ -737,28 +734,46 @@ impl BlankLinesChecker { line.first_token_range, ); - let chars_to_remove = if line.indent_length > 0 { - u32::from(line.blank_lines_len) - BLANK_LINES_METHOD_LEVEL - } else { - u32::from(line.blank_lines_len) - BLANK_LINES_TOP_LEVEL - }; - let end = locator.line_start(line.first_token_range.start()); - let start = end - TextSize::new(chars_to_remove); - diagnostic.set_fix(Fix::safe_edit(Edit::deletion(start, end))); + if let BlankLines::Many { + count: _, + range: blank_lines_range, + } = line.blank_lines + { + if line.indent_length > 0 { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + stylist + .line_ending() + .repeat((BLANK_LINES_METHOD_LEVEL) as usize), + blank_lines_range, + ))); + } else { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + stylist + .line_ending() + .repeat((BLANK_LINES_TOP_LEVEL) as usize), + blank_lines_range, + ))); + }; + } diagnostics.push(diagnostic); } - if matches!(self.follows, Follows::Decorator) && line.preceding_blank_lines > 0 { + if matches!(self.follows, Follows::Decorator) + && !line.is_comment_only + && line.preceding_blank_lines > 0 + { // E304 let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, line.first_token_range); - let range = line.first_token_range; - diagnostic.set_fix(Fix::safe_edit(Edit::deletion( - locator.line_start(range.start()) - line.blank_lines_len, - locator.line_start(range.start()), - ))); + if let BlankLines::Many { + count: _, + range: blank_lines_range, + } = line.preceding_blank_lines + { + diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(blank_lines_range))); + } diagnostics.push(diagnostic); } @@ -779,13 +794,6 @@ impl BlankLinesChecker { line.first_token_range, ); - // diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - // stylist - // .line_ending() - // .repeat((BLANK_LINES_TOP_LEVEL - line.blank_lines) as usize), - // locator.line_start(line.first_token_range.start()), - // ))); - match line.blank_lines { BlankLines::Many { count: _blank_lines, diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 6a336c844b5f1..817dcc5f9390a 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -21,14 +21,13 @@ E30.py:630:1: E304 [*] blank lines found after function decorator 631 630 | pass 632 631 | # end -E30.py:638:1: E304 [*] blank lines found after function decorator +E30.py:639:1: E304 [*] blank lines found after function decorator | -636 | @decorator -637 | 638 | # comment E304 not expected - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E304 639 | def function(): + | ^^^ E304 640 | pass +641 | # end | = help: Remove extraneous blank line(s) @@ -41,16 +40,4 @@ E30.py:638:1: E304 [*] blank lines found after function decorator 639 638 | def function(): 640 639 | pass -E30.py:639:1: E304 [*] blank lines found after function decorator - | -638 | # comment E304 not expected -639 | def function(): - | ^^^ E304 -640 | pass -641 | # end - | - = help: Remove extraneous blank line(s) - -ℹ Safe fix - From c2f6a0c28038e560fae6dc1ff0ed973eaa56576d Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 20 Jan 2024 14:29:49 +0900 Subject: [PATCH 108/122] Add E304 fixture. --- .../test/fixtures/pycodestyle/E30.py | 12 + ...ules__pycodestyle__tests__E304_E30.py.snap | 22 ++ ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 324 +++++++++--------- 4 files changed, 259 insertions(+), 225 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 1b8b195ed0856..61a123e3bad96 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -641,6 +641,18 @@ def function(): # end +# E304 +@decorator + +# comment E304 not expected + + +# second comment E304 not expected +def function(): + pass +# end + + # E305:7:1 def fn(): print() diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 817dcc5f9390a..60b9a67b3e9a5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -40,4 +40,26 @@ E30.py:639:1: E304 [*] blank lines found after function decorator 639 638 | def function(): 640 639 | pass +E30.py:651:1: E304 [*] blank lines found after function decorator + | +650 | # second comment E304 not expected +651 | def function(): + | ^^^ E304 +652 | pass +653 | # end + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +643 643 | +644 644 | # E304 +645 645 | @decorator +646 |- +647 646 | # comment E304 not expected +648 |- +649 |- +650 647 | # second comment E304 not expected +651 648 | def function(): +652 649 | pass + diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index d7c37ca642ebf..536a346331c04 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:651:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:663:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -650 | # another comment -651 | fn() +662 | # another comment +663 | fn() | ^^ E305 -652 | # end +664 | # end | = help: Add missing blank line(s) ℹ Safe fix -648 648 | # comment -649 649 | -650 650 | # another comment - 651 |+ - 652 |+ -651 653 | fn() -652 654 | # end -653 655 | +660 660 | # comment +661 661 | +662 662 | # another comment + 663 |+ + 664 |+ +663 665 | fn() +664 666 | # end +665 667 | -E30.py:662:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:674:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -661 | # another comment -662 | a = 1 +673 | # another comment +674 | a = 1 | ^ E305 -663 | # end +675 | # end | = help: Add missing blank line(s) ℹ Safe fix -659 659 | # comment -660 660 | -661 661 | # another comment - 662 |+ - 663 |+ -662 664 | a = 1 -663 665 | # end -664 666 | +671 671 | # comment +672 672 | +673 673 | # another comment + 674 |+ + 675 |+ +674 676 | a = 1 +675 677 | # end +676 678 | -E30.py:674:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:686:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -672 | # another comment -673 | -674 | try: +684 | # another comment +685 | +686 | try: | ^^^ E305 -675 | fn() -676 | except Exception: +687 | fn() +688 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -671 671 | -672 672 | # another comment -673 673 | - 674 |+ -674 675 | try: -675 676 | fn() -676 677 | except Exception: +683 683 | +684 684 | # another comment +685 685 | + 686 |+ +686 687 | try: +687 688 | fn() +688 689 | except Exception: -E30.py:686:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:698:1: E305 [*] expected 2 blank lines after class or function definition, found (0) | -685 | # Two spaces before comments, too. -686 | if a(): +697 | # Two spaces before comments, too. +698 | if a(): | ^^ E305 -687 | a() -688 | # end +699 | a() +700 | # end | = help: Add missing blank line(s) ℹ Safe fix -683 683 | print -684 684 | -685 685 | # Two spaces before comments, too. - 686 |+ - 687 |+ -686 688 | if a(): -687 689 | a() -688 690 | # end +695 695 | print +696 696 | +697 697 | # Two spaces before comments, too. + 698 |+ + 699 |+ +698 700 | if a(): +699 701 | a() +700 702 | # end -E30.py:699:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:711:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -697 | blah, blah -698 | -699 | if __name__ == '__main__': +709 | blah, blah +710 | +711 | if __name__ == '__main__': | ^^ E305 -700 | main() -701 | # end +712 | main() +713 | # end | = help: Add missing blank line(s) ℹ Safe fix -696 696 | def main(): -697 697 | blah, blah -698 698 | - 699 |+ -699 700 | if __name__ == '__main__': -700 701 | main() -701 702 | # end +708 708 | def main(): +709 709 | blah, blah +710 710 | + 711 |+ +711 712 | if __name__ == '__main__': +712 713 | main() +713 714 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index e554f1dd0b454..1026bc1ea8d2b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,223 +1,223 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:707:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:719:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -705 | def a(): -706 | x = 1 -707 | def b(): +717 | def a(): +718 | x = 1 +719 | def b(): | ^^^ E306 -708 | pass -709 | # end +720 | pass +721 | # end | = help: Add missing blank line ℹ Safe fix -704 704 | # E306:3:5 -705 705 | def a(): -706 706 | x = 1 - 707 |+ -707 708 | def b(): -708 709 | pass -709 710 | # end - -E30.py:715:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -713 | async def a(): -714 | x = 1 -715 | def b(): +716 716 | # E306:3:5 +717 717 | def a(): +718 718 | x = 1 + 719 |+ +719 720 | def b(): +720 721 | pass +721 722 | # end + +E30.py:727:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +725 | async def a(): +726 | x = 1 +727 | def b(): | ^^^ E306 -716 | pass -717 | # end +728 | pass +729 | # end | = help: Add missing blank line ℹ Safe fix -712 712 | #: E306:3:5 -713 713 | async def a(): -714 714 | x = 1 - 715 |+ -715 716 | def b(): -716 717 | pass -717 718 | # end - -E30.py:723:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -721 | def a(): -722 | x = 2 -723 | def b(): +724 724 | #: E306:3:5 +725 725 | async def a(): +726 726 | x = 1 + 727 |+ +727 728 | def b(): +728 729 | pass +729 730 | # end + +E30.py:735:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +733 | def a(): +734 | x = 2 +735 | def b(): | ^^^ E306 -724 | x = 1 -725 | def c(): +736 | x = 1 +737 | def c(): | = help: Add missing blank line ℹ Safe fix -720 720 | #: E306:3:5 E306:5:9 -721 721 | def a(): -722 722 | x = 2 - 723 |+ -723 724 | def b(): -724 725 | x = 1 -725 726 | def c(): - -E30.py:725:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -723 | def b(): -724 | x = 1 -725 | def c(): +732 732 | #: E306:3:5 E306:5:9 +733 733 | def a(): +734 734 | x = 2 + 735 |+ +735 736 | def b(): +736 737 | x = 1 +737 738 | def c(): + +E30.py:737:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +735 | def b(): +736 | x = 1 +737 | def c(): | ^^^ E306 -726 | pass -727 | # end +738 | pass +739 | # end | = help: Add missing blank line ℹ Safe fix -722 722 | x = 2 -723 723 | def b(): -724 724 | x = 1 - 725 |+ -725 726 | def c(): -726 727 | pass -727 728 | # end - -E30.py:733:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -731 | def a(): -732 | x = 1 -733 | class C: +734 734 | x = 2 +735 735 | def b(): +736 736 | x = 1 + 737 |+ +737 738 | def c(): +738 739 | pass +739 740 | # end + +E30.py:745:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +743 | def a(): +744 | x = 1 +745 | class C: | ^^^^^ E306 -734 | pass -735 | x = 2 +746 | pass +747 | x = 2 | = help: Add missing blank line ℹ Safe fix -730 730 | # E306:3:5 E306:6:5 -731 731 | def a(): -732 732 | x = 1 - 733 |+ -733 734 | class C: -734 735 | pass -735 736 | x = 2 - -E30.py:736:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -734 | pass -735 | x = 2 -736 | def b(): +742 742 | # E306:3:5 E306:6:5 +743 743 | def a(): +744 744 | x = 1 + 745 |+ +745 746 | class C: +746 747 | pass +747 748 | x = 2 + +E30.py:748:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +746 | pass +747 | x = 2 +748 | def b(): | ^^^ E306 -737 | pass -738 | # end +749 | pass +750 | # end | = help: Add missing blank line ℹ Safe fix -733 733 | class C: -734 734 | pass -735 735 | x = 2 - 736 |+ -736 737 | def b(): -737 738 | pass -738 739 | # end - -E30.py:745:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -743 | def bar(): -744 | pass -745 | def baz(): pass +745 745 | class C: +746 746 | pass +747 747 | x = 2 + 748 |+ +748 749 | def b(): +749 750 | pass +750 751 | # end + +E30.py:757:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +755 | def bar(): +756 | pass +757 | def baz(): pass | ^^^ E306 -746 | # end +758 | # end | = help: Add missing blank line ℹ Safe fix -742 742 | def foo(): -743 743 | def bar(): -744 744 | pass - 745 |+ -745 746 | def baz(): pass -746 747 | # end -747 748 | - -E30.py:752:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -750 | def foo(): -751 | def bar(): pass -752 | def baz(): +754 754 | def foo(): +755 755 | def bar(): +756 756 | pass + 757 |+ +757 758 | def baz(): pass +758 759 | # end +759 760 | + +E30.py:764:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +762 | def foo(): +763 | def bar(): pass +764 | def baz(): | ^^^ E306 -753 | pass -754 | # end +765 | pass +766 | # end | = help: Add missing blank line ℹ Safe fix -749 749 | # E306:3:5 -750 750 | def foo(): -751 751 | def bar(): pass - 752 |+ -752 753 | def baz(): -753 754 | pass -754 755 | # end - -E30.py:760:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -758 | def a(): -759 | x = 2 -760 | @decorator +761 761 | # E306:3:5 +762 762 | def foo(): +763 763 | def bar(): pass + 764 |+ +764 765 | def baz(): +765 766 | pass +766 767 | # end + +E30.py:772:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +770 | def a(): +771 | x = 2 +772 | @decorator | ^ E306 -761 | def b(): -762 | pass +773 | def b(): +774 | pass | = help: Add missing blank line ℹ Safe fix -757 757 | # E306 -758 758 | def a(): -759 759 | x = 2 - 760 |+ -760 761 | @decorator -761 762 | def b(): -762 763 | pass - -E30.py:769:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -767 | def a(): -768 | x = 2 -769 | @decorator +769 769 | # E306 +770 770 | def a(): +771 771 | x = 2 + 772 |+ +772 773 | @decorator +773 774 | def b(): +774 775 | pass + +E30.py:781:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +779 | def a(): +780 | x = 2 +781 | @decorator | ^ E306 -770 | async def b(): -771 | pass +782 | async def b(): +783 | pass | = help: Add missing blank line ℹ Safe fix -766 766 | # E306 -767 767 | def a(): -768 768 | x = 2 - 769 |+ -769 770 | @decorator -770 771 | async def b(): -771 772 | pass - -E30.py:778:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -776 | def a(): -777 | x = 2 -778 | async def b(): +778 778 | # E306 +779 779 | def a(): +780 780 | x = 2 + 781 |+ +781 782 | @decorator +782 783 | async def b(): +783 784 | pass + +E30.py:790:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +788 | def a(): +789 | x = 2 +790 | async def b(): | ^^^^^ E306 -779 | pass -780 | # end +791 | pass +792 | # end | = help: Add missing blank line ℹ Safe fix -775 775 | # E306 -776 776 | def a(): -777 777 | x = 2 - 778 |+ -778 779 | async def b(): -779 780 | pass -780 781 | # end +787 787 | # E306 +788 788 | def a(): +789 789 | x = 2 + 790 |+ +790 791 | async def b(): +791 792 | pass +792 793 | # end From 7ee8946ad448497ff92e4e19db51ba8e67ef20f6 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sat, 20 Jan 2024 14:37:38 +0900 Subject: [PATCH 109/122] Improve E304 fix. --- .../rules/pycodestyle/rules/blank_lines.rs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 1400bc60214af..9315a7a537ec1 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -17,6 +17,7 @@ use ruff_text_size::TextSize; use crate::checkers::logical_lines::expand_indent; use crate::line_width::IndentWidth; +use regex::Regex; /// Number of blank lines around top level classes and functions. const BLANK_LINES_TOP_LEVEL: u32 = 2; @@ -767,13 +768,23 @@ impl BlankLinesChecker { let mut diagnostic = Diagnostic::new(BlankLineAfterDecorator, line.first_token_range); - if let BlankLines::Many { - count: _, - range: blank_lines_range, - } = line.preceding_blank_lines - { - diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(blank_lines_range))); - } + // Get all the lines between the last decorator line (included) and the current line (included). + // Then remove all blank lines. + let trivia_range = locator.lines_range(TextRange::new( + self.last_non_comment_line_end - stylist.line_ending().text_len(), + line.first_token_range.start(), + )); + let text = locator.full_lines(trivia_range); + let pattern = Regex::new(r"\n+").unwrap(); + let result_string = pattern.replace_all(text, "\n"); + + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + result_string.to_string(), + TextRange::new( + trivia_range.start(), + trivia_range.end() + stylist.line_ending().text_len(), + ), + ))); diagnostics.push(diagnostic); } From 24bf58599c9b2f84e092e622581d41a3feff21c9 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Sun, 21 Jan 2024 22:34:25 +0900 Subject: [PATCH 110/122] Match pycodestyle error message for E305. --- .../resources/test/fixtures/pycodestyle/E30.py | 2 +- .../src/rules/pycodestyle/rules/blank_lines.rs | 2 +- ...ff_linter__rules__pycodestyle__tests__E305_E30.py.snap | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 61a123e3bad96..82d8f47db9466 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -692,7 +692,7 @@ def fn(): # E305:5:1 def a(): - print + print() # Two spaces before comments, too. if a(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 9315a7a537ec1..daae14d1cd899 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -800,7 +800,7 @@ impl BlankLinesChecker { // E305 let mut diagnostic = Diagnostic::new( BlankLinesAfterFunctionOrClass { - actual_blank_lines: line.blank_lines.count(), + actual_blank_lines: line.preceding_blank_lines.count(), }, line.first_token_range, ); diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 536a346331c04..0981e4406115f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:663:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:663:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | 662 | # another comment 663 | fn() @@ -20,7 +20,7 @@ E30.py:663:1: E305 [*] expected 2 blank lines after class or function definition 664 666 | # end 665 667 | -E30.py:674:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:674:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | 673 | # another comment 674 | a = 1 @@ -59,7 +59,7 @@ E30.py:686:1: E305 [*] expected 2 blank lines after class or function definition 687 688 | fn() 688 689 | except Exception: -E30.py:698:1: E305 [*] expected 2 blank lines after class or function definition, found (0) +E30.py:698:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | 697 | # Two spaces before comments, too. 698 | if a(): @@ -70,7 +70,7 @@ E30.py:698:1: E305 [*] expected 2 blank lines after class or function definition = help: Add missing blank line(s) ℹ Safe fix -695 695 | print +695 695 | print() 696 696 | 697 697 | # Two spaces before comments, too. 698 |+ From 71d905766e2492a037baedb0b06fd01f6ee89583 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Mon, 22 Jan 2024 21:45:42 +0900 Subject: [PATCH 111/122] docs: add comment about preceding_blank_lines vs blank_lines. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index daae14d1cd899..9b57d6bb12488 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -335,7 +335,9 @@ struct LogicalLineInfo { /// The maximum number of consecutive blank lines between the current line /// and the previous non-comment logical line. - /// One of its main uses is to allow a comment to directly precede a class/function definition. + /// One of its main uses is to allow a comments to directly precede or follow a class/function definition. + /// As such, `preceding_blank_lines` is used for rules that cannot trigger on comments (all rules except E303), + /// and `blank_lines` is used for the rule that can trigger on comments (E303). preceding_blank_lines: BlankLines, } From 8392f204ccd1bd0b8ed5d870fdf406f8f47f1487 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 5 Feb 2024 17:45:13 -0500 Subject: [PATCH 112/122] Move status update logic into its own function, mark rules as preview --- crates/ruff_linter/src/codes.rs | 18 +-- .../rules/pycodestyle/rules/blank_lines.rs | 115 +++++++++--------- 2 files changed, 65 insertions(+), 68 deletions(-) diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 58b3aebe0cd4f..d79d21dcf7a26 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -137,18 +137,12 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pycodestyle, "E274") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::TabBeforeKeyword), #[allow(deprecated)] (Pycodestyle, "E275") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::MissingWhitespaceAfterKeyword), - #[allow(deprecated)] - (Pycodestyle, "E301") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLineBetweenMethods), - #[allow(deprecated)] - (Pycodestyle, "E302") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLinesTopLevel), - #[allow(deprecated)] - (Pycodestyle, "E303") => (RuleGroup::Nursery, rules::pycodestyle::rules::TooManyBlankLines), - #[allow(deprecated)] - (Pycodestyle, "E304") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLineAfterDecorator), - #[allow(deprecated)] - (Pycodestyle, "E305") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLinesAfterFunctionOrClass), - #[allow(deprecated)] - (Pycodestyle, "E306") => (RuleGroup::Nursery, rules::pycodestyle::rules::BlankLinesBeforeNestedDefinition), + (Pycodestyle, "E301") => (RuleGroup::Preview, rules::pycodestyle::rules::BlankLineBetweenMethods), + (Pycodestyle, "E302") => (RuleGroup::Preview, rules::pycodestyle::rules::BlankLinesTopLevel), + (Pycodestyle, "E303") => (RuleGroup::Preview, rules::pycodestyle::rules::TooManyBlankLines), + (Pycodestyle, "E304") => (RuleGroup::Preview, rules::pycodestyle::rules::BlankLineAfterDecorator), + (Pycodestyle, "E305") => (RuleGroup::Preview, rules::pycodestyle::rules::BlankLinesAfterFunctionOrClass), + (Pycodestyle, "E306") => (RuleGroup::Preview, rules::pycodestyle::rules::BlankLinesBeforeNestedDefinition), (Pycodestyle, "E401") => (RuleGroup::Stable, rules::pycodestyle::rules::MultipleImportsOnOneLine), (Pycodestyle, "E402") => (RuleGroup::Stable, rules::pycodestyle::rules::ModuleImportNotAtTopOfFile), (Pycodestyle, "E501") => (RuleGroup::Stable, rules::pycodestyle::rules::LineTooLong), diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 9b57d6bb12488..9ceb66ee2fbe7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -1,3 +1,4 @@ +use std::cmp::Ordering; use std::num::NonZeroU32; use std::slice::Iter; @@ -328,6 +329,8 @@ struct LogicalLineInfo { /// `true` if the line is a string only (including trivia tokens) line, which is a docstring if coming right after a class/function definition. is_docstring: bool, + + /// The indentation length in columns. See [`expand_indent`] for the computation of the indent. indent_length: usize, /// The number of blank lines preceding the current line. @@ -378,7 +381,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { let mut is_docstring = false; // Number of consecutive blank lines directly preceding this logical line. let mut blank_lines = BlankLines::Zero; - let mut logical_line_start: Option<(LogicalLineKind, TextRange)> = None; + let mut first_logical_line_token: Option<(LogicalLineKind, TextRange)> = None; let mut last_token: TokenKind = TokenKind::EndOfFile; let mut parens = 0u32; @@ -394,7 +397,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { let token_kind = TokenKind::from_token(token); let (logical_line_kind, first_token_range) = if let Some(first_token_range) = - logical_line_start + first_logical_line_token { first_token_range } @@ -425,7 +428,7 @@ impl<'a> Iterator for LinePreprocessor<'a> { _ => LogicalLineKind::Other, }; - logical_line_start = Some((logical_line_kind, *range)); + first_logical_line_token = Some((logical_line_kind, *range)); (logical_line_kind, *range) }; @@ -453,9 +456,8 @@ impl<'a> Iterator for LinePreprocessor<'a> { let indent_length = expand_indent(self.locator.slice(indent_range), self.indent_width); - if blank_lines.count() > self.max_preceding_blank_lines.count() { - self.max_preceding_blank_lines = blank_lines; - } + self.max_preceding_blank_lines = + self.max_preceding_blank_lines.max(blank_lines); let logical_line = LogicalLineInfo { kind: logical_line_kind, @@ -524,14 +526,9 @@ impl BlankLines { } } -use std::cmp::Ordering; - impl PartialEq for BlankLines { fn eq(&self, other: &u32) -> bool { - match self { - BlankLines::Zero => *other == 0, - BlankLines::Many { count, range: _ } => count.get() == *other, - } + self.partial_cmp(other) == Some(Ordering::Equal) } } @@ -541,6 +538,26 @@ impl PartialOrd for BlankLines { } } +impl PartialOrd for BlankLines { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for BlankLines { + fn cmp(&self, other: &Self) -> Ordering { + self.count().cmp(&other.count()) + } +} + +impl PartialEq for BlankLines { + fn eq(&self, other: &Self) -> bool { + self.count() == other.count() + } +} + +impl Eq for BlankLines {} + #[derive(Copy, Clone, Debug, Default)] enum Follows { #[default] @@ -560,6 +577,34 @@ enum Status { Outside, } +impl Status { + fn update(&mut self, line: &LogicalLineInfo) { + match *self { + Status::Inside(nesting_indent) => { + if line.indent_length <= nesting_indent { + if line.is_comment_only { + *self = Status::CommentAfter(nesting_indent); + } else { + *self = Status::Outside; + } + } + } + Status::CommentAfter(indent) => { + if !line.is_comment_only { + if line.indent_length > indent { + *self = Status::Inside(indent); + } else { + *self = Status::Outside; + } + } + } + Status::Outside => { + // Nothing to do + } + } + } +} + /// Contains variables used for the linting of blank lines. #[derive(Debug, Default)] pub(crate) struct BlankLinesChecker { @@ -610,50 +655,8 @@ impl BlankLinesChecker { stylist: &Stylist, diagnostics: &mut Vec, ) { - match self.class_status { - Status::Inside(nesting_indent) => { - if line.indent_length <= nesting_indent { - if line.is_comment_only { - self.class_status = Status::CommentAfter(nesting_indent); - } else { - self.class_status = Status::Outside; - } - } - } - Status::CommentAfter(indent) => { - if !line.is_comment_only { - if line.indent_length > indent { - self.class_status = Status::Inside(indent); - } - self.class_status = Status::Outside; - } - } - Status::Outside => { - // Nothing to do - } - } - - if let Status::Inside(nesting_indent) = self.fn_status { - if line.indent_length <= nesting_indent { - if line.is_comment_only { - self.fn_status = Status::CommentAfter(nesting_indent); - } else { - self.fn_status = Status::Outside; - } - } - } - - // A comment can be de-indented while still being in a class/function, in that case - // we need to revert the variables. - if !line.is_comment_only { - if let Status::CommentAfter(indent) = self.fn_status { - if line.indent_length > indent { - self.fn_status = Status::Inside(indent); - } else { - self.fn_status = Status::Outside; - } - } - } + self.class_status.update(line); + self.fn_status.update(line); // Don't expect blank lines before the first non comment line. if self.is_not_first_logical_line { From 0ddcca433d13a67140b2055ae130de3d57df0c93 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 5 Feb 2024 18:22:29 -0500 Subject: [PATCH 113/122] Introduce `blank_lines.range` method --- .../rules/pycodestyle/rules/blank_lines.rs | 82 ++++++++----------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 9ceb66ee2fbe7..42160e76e6253 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -524,6 +524,13 @@ impl BlankLines { BlankLines::Many { count, .. } => count.get(), } } + + fn range(&self) -> Option { + match self { + BlankLines::Zero => None, + BlankLines::Many { range, .. } => Some(*range), + } + } } impl PartialEq for BlankLines { @@ -665,16 +672,15 @@ impl BlankLinesChecker { && matches!(line.kind, LogicalLineKind::Function) && matches!(self.class_status, Status::Inside(_)) // The class/parent method's docstring can directly precede the def. - && !matches!(self.follows, Follows::Docstring) + // Allow following a decorator (if there is an error it will be triggered on the first decorator). + && !matches!(self.follows, Follows::Docstring | Follows::Decorator) // Do not trigger when the def follows an if/while/etc... && prev_indent_length.is_some_and(|prev_indent_length| prev_indent_length >= line.indent_length) - // Allow following a decorator (if there is an error it will be triggered on the first decorator). - && !matches!(self.follows, Follows::Decorator) { // E301 let mut diagnostic = Diagnostic::new( BlankLineBetweenMethods { - actual_blank_lines: line.preceding_blank_lines.count(), + actual_blank_lines: 0, }, line.first_token_range, ); @@ -704,26 +710,16 @@ impl BlankLinesChecker { line.first_token_range, ); - match line.blank_lines { - BlankLines::Many { - count: _blank_lines, - range: blank_lines_range, - } => { - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - stylist - .line_ending() - .repeat((BLANK_LINES_TOP_LEVEL) as usize), - blank_lines_range, - ))); - } - BlankLines::Zero => { - diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .repeat((BLANK_LINES_TOP_LEVEL) as usize), - locator.line_start(self.last_non_comment_line_end), - ))); - } + if let Some(blank_lines_range) = line.blank_lines.range() { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + stylist.line_ending().repeat(BLANK_LINES_TOP_LEVEL as usize), + blank_lines_range, + ))); + } else { + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().repeat(BLANK_LINES_TOP_LEVEL as usize), + locator.line_start(self.last_non_comment_line_end), + ))); } diagnostics.push(diagnostic); @@ -740,23 +736,17 @@ impl BlankLinesChecker { line.first_token_range, ); - if let BlankLines::Many { - count: _, - range: blank_lines_range, - } = line.blank_lines - { + if let Some(blank_lines_range) = line.blank_lines.range() { if line.indent_length > 0 { diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( stylist .line_ending() - .repeat((BLANK_LINES_METHOD_LEVEL) as usize), + .repeat(BLANK_LINES_METHOD_LEVEL as usize), blank_lines_range, ))); } else { diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - stylist - .line_ending() - .repeat((BLANK_LINES_TOP_LEVEL) as usize), + stylist.line_ending().repeat(BLANK_LINES_TOP_LEVEL as usize), blank_lines_range, ))); }; @@ -810,24 +800,16 @@ impl BlankLinesChecker { line.first_token_range, ); - match line.blank_lines { - BlankLines::Many { - count: _blank_lines, - range: blank_lines_range, - } => { - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - stylist - .line_ending() - .repeat((BLANK_LINES_TOP_LEVEL) as usize), - blank_lines_range, - ))); - } - BlankLines::Zero => diagnostic.set_fix(Fix::safe_edit(Edit::insertion( - stylist - .line_ending() - .repeat((BLANK_LINES_TOP_LEVEL) as usize), + if let Some(blank_lines_range) = line.blank_lines.range() { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + stylist.line_ending().repeat(BLANK_LINES_TOP_LEVEL as usize), + blank_lines_range, + ))); + } else { + diagnostic.set_fix(Fix::safe_edit(Edit::insertion( + stylist.line_ending().repeat(BLANK_LINES_TOP_LEVEL as usize), locator.line_start(line.first_token_range.start()), - ))), + ))) } diagnostics.push(diagnostic); From de559d8c92b900e94c0e0d2776b4597be9923e4c Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 5 Feb 2024 20:23:12 -0500 Subject: [PATCH 114/122] Use `universal_newlines` for decorator fix --- .../rules/pycodestyle/rules/blank_lines.rs | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 42160e76e6253..7fbff47cb1f6a 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use std::cmp::Ordering; use std::num::NonZeroU32; use std::slice::Iter; @@ -12,13 +13,13 @@ use ruff_python_parser::lexer::LexResult; use ruff_python_parser::lexer::LexicalError; use ruff_python_parser::Tok; use ruff_python_parser::TokenKind; -use ruff_source_file::Locator; +use ruff_source_file::{Locator, UniversalNewlines}; use ruff_text_size::TextRange; use ruff_text_size::TextSize; use crate::checkers::logical_lines::expand_indent; use crate::line_width::IndentWidth; -use regex::Regex; +use ruff_python_trivia::PythonWhitespace; /// Number of blank lines around top level classes and functions. const BLANK_LINES_TOP_LEVEL: u32 = 2; @@ -765,21 +766,29 @@ impl BlankLinesChecker { // Get all the lines between the last decorator line (included) and the current line (included). // Then remove all blank lines. - let trivia_range = locator.lines_range(TextRange::new( - self.last_non_comment_line_end - stylist.line_ending().text_len(), + let trivia_range = TextRange::new( + self.last_non_comment_line_end, line.first_token_range.start(), - )); - let text = locator.full_lines(trivia_range); - let pattern = Regex::new(r"\n+").unwrap(); - let result_string = pattern.replace_all(text, "\n"); - - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - result_string.to_string(), - TextRange::new( - trivia_range.start(), - trivia_range.end() + stylist.line_ending().text_len(), - ), - ))); + ); + let trivia_text = locator.slice(trivia_range); + let mut trivia_without_blank_lines = trivia_text + .universal_newlines() + .filter_map(|line| { + (!line.trim_whitespace().is_empty()).then_some(line.as_str()) + }) + .join(&stylist.line_ending()); + + let fix = if trivia_without_blank_lines.is_empty() { + Fix::safe_edit(Edit::range_deletion(trivia_range)) + } else { + trivia_without_blank_lines.push_str(&stylist.line_ending()); + Fix::safe_edit(Edit::range_replacement( + trivia_without_blank_lines, + trivia_range, + )) + }; + + diagnostic.set_fix(fix); diagnostics.push(diagnostic); } From db137d91c4cb83f053ce3fa74c2c6e11ee29a7dd Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Mon, 5 Feb 2024 20:40:10 -0500 Subject: [PATCH 115/122] Small code simpliciations --- .../rules/pycodestyle/rules/blank_lines.rs | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 7fbff47cb1f6a..aa2ad632c28eb 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -726,9 +726,13 @@ impl BlankLinesChecker { diagnostics.push(diagnostic); } - if line.blank_lines > BLANK_LINES_TOP_LEVEL - || (line.indent_length > 0 && line.blank_lines > BLANK_LINES_METHOD_LEVEL) - { + let expected_blank_lines = if line.indent_length > 0 { + BLANK_LINES_METHOD_LEVEL + } else { + BLANK_LINES_TOP_LEVEL + }; + + if line.blank_lines > expected_blank_lines { // E303 let mut diagnostic = Diagnostic::new( TooManyBlankLines { @@ -738,19 +742,10 @@ impl BlankLinesChecker { ); if let Some(blank_lines_range) = line.blank_lines.range() { - if line.indent_length > 0 { - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - stylist - .line_ending() - .repeat(BLANK_LINES_METHOD_LEVEL as usize), - blank_lines_range, - ))); - } else { - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - stylist.line_ending().repeat(BLANK_LINES_TOP_LEVEL as usize), - blank_lines_range, - ))); - }; + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + stylist.line_ending().repeat(expected_blank_lines as usize), + blank_lines_range, + ))); } diagnostics.push(diagnostic); @@ -768,7 +763,7 @@ impl BlankLinesChecker { // Then remove all blank lines. let trivia_range = TextRange::new( self.last_non_comment_line_end, - line.first_token_range.start(), + locator.line_start(line.first_token_range.start()), ); let trivia_text = locator.slice(trivia_range); let mut trivia_without_blank_lines = trivia_text @@ -818,7 +813,7 @@ impl BlankLinesChecker { diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().repeat(BLANK_LINES_TOP_LEVEL as usize), locator.line_start(line.first_token_range.start()), - ))) + ))); } diagnostics.push(diagnostic); From 85302d5d91697b1e9eb21dc5c9d15d013acd22bf Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Tue, 6 Feb 2024 20:28:05 +0900 Subject: [PATCH 116/122] add wrongly indented comment test --- .../test/fixtures/pycodestyle/E30.py | 24 ++ ...ules__pycodestyle__tests__E301_E30.py.snap | 52 +-- ...ules__pycodestyle__tests__E302_E30.py.snap | 250 +++++++------- ...ules__pycodestyle__tests__E303_E30.py.snap | 254 +++++++------- ...ules__pycodestyle__tests__E304_E30.py.snap | 80 ++--- ...ules__pycodestyle__tests__E305_E30.py.snap | 126 +++---- ...ules__pycodestyle__tests__E306_E30.py.snap | 324 +++++++++--------- 7 files changed, 576 insertions(+), 534 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py index 82d8f47db9466..37c2e6d803ce7 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E30.py @@ -425,6 +425,17 @@ def a(self): pass # end +# no error +class Test: + def a(): + pass +# wrongly indented comment + + def b(): + pass +# end + + # E301 class Class(object): @@ -624,6 +635,19 @@ def test(self): pass # end +# E303 +class Test: + def a(self): + pass + +# wrongly indented comment + + + def b(self): + pass +# end + + # E304 @decorator diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index a286249e86784..5e3e0713fc108 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -1,44 +1,44 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:433:5: E301 [*] Expected 1 blank line, found 0 +E30.py:444:5: E301 [*] Expected 1 blank line, found 0 | -431 | def func1(): -432 | pass -433 | def func2(): +442 | def func1(): +443 | pass +444 | def func2(): | ^^^ E301 -434 | pass -435 | # end +445 | pass +446 | # end | = help: Add missing blank line(s) ℹ Safe fix -430 430 | -431 431 | def func1(): -432 432 | pass - 433 |+ -433 434 | def func2(): -434 435 | pass -435 436 | # end +441 441 | +442 442 | def func1(): +443 443 | pass + 444 |+ +444 445 | def func2(): +445 446 | pass +446 447 | # end -E30.py:444:5: E301 [*] Expected 1 blank line, found 0 +E30.py:455:5: E301 [*] Expected 1 blank line, found 0 | -442 | pass -443 | # comment -444 | def fn2(): +453 | pass +454 | # comment +455 | def fn2(): | ^^^ E301 -445 | pass -446 | # end +456 | pass +457 | # end | = help: Add missing blank line(s) ℹ Safe fix -440 440 | -441 441 | def fn1(): -442 442 | pass - 443 |+ -443 444 | # comment -444 445 | def fn2(): -445 446 | pass +451 451 | +452 452 | def fn1(): +453 453 | pass + 454 |+ +454 455 | # comment +455 456 | def fn2(): +456 457 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap index a6a4fb871af5f..24311cccff3ed 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E302_E30.py.snap @@ -1,71 +1,51 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:451:1: E302 [*] Expected 2 blank lines, found 0 +E30.py:462:1: E302 [*] Expected 2 blank lines, found 0 | -449 | # E302 -450 | """Main module.""" -451 | def fn(): +460 | # E302 +461 | """Main module.""" +462 | def fn(): | ^^^ E302 -452 | pass -453 | # end +463 | pass +464 | # end | = help: Add missing blank line(s) ℹ Safe fix -448 448 | -449 449 | # E302 -450 450 | """Main module.""" - 451 |+ - 452 |+ -451 453 | def fn(): -452 454 | pass -453 455 | # end - -E30.py:458:1: E302 [*] Expected 2 blank lines, found 0 - | -456 | # E302 -457 | import sys -458 | def get_sys_path(): +459 459 | +460 460 | # E302 +461 461 | """Main module.""" + 462 |+ + 463 |+ +462 464 | def fn(): +463 465 | pass +464 466 | # end + +E30.py:469:1: E302 [*] Expected 2 blank lines, found 0 + | +467 | # E302 +468 | import sys +469 | def get_sys_path(): | ^^^ E302 -459 | return sys.path -460 | # end +470 | return sys.path +471 | # end | = help: Add missing blank line(s) ℹ Safe fix -455 455 | -456 456 | # E302 -457 457 | import sys - 458 |+ - 459 |+ -458 460 | def get_sys_path(): -459 461 | return sys.path -460 462 | # end - -E30.py:467:1: E302 [*] Expected 2 blank lines, found 1 - | -465 | pass -466 | -467 | def b(): - | ^^^ E302 -468 | pass -469 | # end - | - = help: Add missing blank line(s) - -ℹ Safe fix -464 464 | def a(): -465 465 | pass 466 466 | - 467 |+ -467 468 | def b(): -468 469 | pass -469 470 | # end +467 467 | # E302 +468 468 | import sys + 469 |+ + 470 |+ +469 471 | def get_sys_path(): +470 472 | return sys.path +471 473 | # end E30.py:478:1: E302 [*] Expected 2 blank lines, found 1 | -476 | # comment +476 | pass 477 | 478 | def b(): | ^^^ E302 @@ -75,113 +55,133 @@ E30.py:478:1: E302 [*] Expected 2 blank lines, found 1 = help: Add missing blank line(s) ℹ Safe fix -475 475 | -476 476 | # comment +475 475 | def a(): +476 476 | pass 477 477 | 478 |+ 478 479 | def b(): 479 480 | pass 480 481 | # end -E30.py:487:1: E302 [*] Expected 2 blank lines, found 1 +E30.py:489:1: E302 [*] Expected 2 blank lines, found 1 | -485 | pass -486 | -487 | async def b(): - | ^^^^^ E302 -488 | pass -489 | # end +487 | # comment +488 | +489 | def b(): + | ^^^ E302 +490 | pass +491 | # end | = help: Add missing blank line(s) ℹ Safe fix -484 484 | def a(): -485 485 | pass 486 486 | - 487 |+ -487 488 | async def b(): -488 489 | pass -489 490 | # end - -E30.py:496:1: E302 [*] Expected 2 blank lines, found 1 +487 487 | # comment +488 488 | + 489 |+ +489 490 | def b(): +490 491 | pass +491 492 | # end + +E30.py:498:1: E302 [*] Expected 2 blank lines, found 1 + | +496 | pass +497 | +498 | async def b(): + | ^^^^^ E302 +499 | pass +500 | # end | -494 | pass -495 | -496 | async def x(y: int = 1): + = help: Add missing blank line(s) + +ℹ Safe fix +495 495 | def a(): +496 496 | pass +497 497 | + 498 |+ +498 499 | async def b(): +499 500 | pass +500 501 | # end + +E30.py:507:1: E302 [*] Expected 2 blank lines, found 1 + | +505 | pass +506 | +507 | async def x(y: int = 1): | ^^^^^ E302 -497 | pass -498 | # end +508 | pass +509 | # end | = help: Add missing blank line(s) ℹ Safe fix -493 493 | async def x(): -494 494 | pass -495 495 | - 496 |+ -496 497 | async def x(y: int = 1): -497 498 | pass -498 499 | # end - -E30.py:504:1: E302 [*] Expected 2 blank lines, found 0 - | -502 | def bar(): -503 | pass -504 | def baz(): pass +504 504 | async def x(): +505 505 | pass +506 506 | + 507 |+ +507 508 | async def x(y: int = 1): +508 509 | pass +509 510 | # end + +E30.py:515:1: E302 [*] Expected 2 blank lines, found 0 + | +513 | def bar(): +514 | pass +515 | def baz(): pass | ^^^ E302 -505 | # end +516 | # end | = help: Add missing blank line(s) ℹ Safe fix -501 501 | # E302 -502 502 | def bar(): -503 503 | pass - 504 |+ - 505 |+ -504 506 | def baz(): pass -505 507 | # end -506 508 | - -E30.py:510:1: E302 [*] Expected 2 blank lines, found 0 - | -508 | # E302 -509 | def bar(): pass -510 | def baz(): +512 512 | # E302 +513 513 | def bar(): +514 514 | pass + 515 |+ + 516 |+ +515 517 | def baz(): pass +516 518 | # end +517 519 | + +E30.py:521:1: E302 [*] Expected 2 blank lines, found 0 + | +519 | # E302 +520 | def bar(): pass +521 | def baz(): | ^^^ E302 -511 | pass -512 | # end +522 | pass +523 | # end | = help: Add missing blank line(s) ℹ Safe fix -507 507 | -508 508 | # E302 -509 509 | def bar(): pass - 510 |+ - 511 |+ -510 512 | def baz(): -511 513 | pass -512 514 | # end - -E30.py:520:1: E302 [*] Expected 2 blank lines, found 1 - | -519 | # comment -520 | @decorator +518 518 | +519 519 | # E302 +520 520 | def bar(): pass + 521 |+ + 522 |+ +521 523 | def baz(): +522 524 | pass +523 525 | # end + +E30.py:531:1: E302 [*] Expected 2 blank lines, found 1 + | +530 | # comment +531 | @decorator | ^ E302 -521 | def g(): -522 | pass +532 | def g(): +533 | pass | = help: Add missing blank line(s) ℹ Safe fix -516 516 | def f(): -517 517 | pass -518 518 | - 519 |+ - 520 |+ -519 521 | # comment -520 522 | @decorator -521 523 | def g(): +527 527 | def f(): +528 528 | pass +529 529 | + 530 |+ + 531 |+ +530 532 | # comment +531 533 | @decorator +532 534 | def g(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap index 58beadf153d8c..e6d6555838263 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E303_E30.py.snap @@ -1,197 +1,215 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:531:5: E303 [*] Too many blank lines (2) +E30.py:542:5: E303 [*] Too many blank lines (2) | -531 | # arbitrary comment +542 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -532 | -533 | def inner(): # E306 not expected (pycodestyle detects E306) +543 | +544 | def inner(): # E306 not expected (pycodestyle detects E306) | = help: Remove extraneous blank line(s) ℹ Safe fix -527 527 | def fn(): -528 528 | _ = None -529 529 | -530 |- -531 530 | # arbitrary comment -532 531 | -533 532 | def inner(): # E306 not expected (pycodestyle detects E306) +538 538 | def fn(): +539 539 | _ = None +540 540 | +541 |- +542 541 | # arbitrary comment +543 542 | +544 543 | def inner(): # E306 not expected (pycodestyle detects E306) -E30.py:543:5: E303 [*] Too many blank lines (2) +E30.py:554:5: E303 [*] Too many blank lines (2) | -543 | # arbitrary comment +554 | # arbitrary comment | ^^^^^^^^^^^^^^^^^^^ E303 -544 | def inner(): # E306 not expected (pycodestyle detects E306) -545 | pass +555 | def inner(): # E306 not expected (pycodestyle detects E306) +556 | pass | = help: Remove extraneous blank line(s) ℹ Safe fix -539 539 | def fn(): -540 540 | _ = None -541 541 | -542 |- -543 542 | # arbitrary comment -544 543 | def inner(): # E306 not expected (pycodestyle detects E306) -545 544 | pass +550 550 | def fn(): +551 551 | _ = None +552 552 | +553 |- +554 553 | # arbitrary comment +555 554 | def inner(): # E306 not expected (pycodestyle detects E306) +556 555 | pass -E30.py:554:1: E303 [*] Too many blank lines (3) +E30.py:565:1: E303 [*] Too many blank lines (3) | -554 | print() +565 | print() | ^^^^^ E303 -555 | # end +566 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -550 550 | print() -551 551 | -552 552 | -553 |- -554 553 | print() -555 554 | # end -556 555 | +561 561 | print() +562 562 | +563 563 | +564 |- +565 564 | print() +566 565 | # end +567 566 | -E30.py:563:1: E303 [*] Too many blank lines (3) +E30.py:574:1: E303 [*] Too many blank lines (3) | -563 | # comment +574 | # comment | ^^^^^^^^^ E303 -564 | -565 | print() +575 | +576 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -559 559 | print() -560 560 | -561 561 | -562 |- -563 562 | # comment -564 563 | -565 564 | print() +570 570 | print() +571 571 | +572 572 | +573 |- +574 573 | # comment +575 574 | +576 575 | print() -E30.py:574:5: E303 [*] Too many blank lines (2) +E30.py:585:5: E303 [*] Too many blank lines (2) | -574 | # comment +585 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -570 570 | def a(): -571 571 | print() -572 572 | -573 |- -574 573 | # comment -575 574 | -576 575 | +581 581 | def a(): +582 582 | print() +583 583 | +584 |- +585 584 | # comment +586 585 | +587 586 | -E30.py:577:5: E303 [*] Too many blank lines (2) +E30.py:588:5: E303 [*] Too many blank lines (2) | -577 | # another comment +588 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -578 | -579 | print() +589 | +590 | print() | = help: Remove extraneous blank line(s) ℹ Safe fix -573 573 | -574 574 | # comment -575 575 | -576 |- -577 576 | # another comment -578 577 | -579 578 | print() - -E30.py:588:1: E303 [*] Too many blank lines (3) - | -588 | / """This class docstring comes on line 5. -589 | | It gives error E303: too many blank lines (3) -590 | | """ +584 584 | +585 585 | # comment +586 586 | +587 |- +588 587 | # another comment +589 588 | +590 589 | print() + +E30.py:599:1: E303 [*] Too many blank lines (3) + | +599 | / """This class docstring comes on line 5. +600 | | It gives error E303: too many blank lines (3) +601 | | """ | |___^ E303 -591 | # end +602 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -584 584 | #!python -585 585 | -586 586 | -587 |- -588 587 | """This class docstring comes on line 5. -589 588 | It gives error E303: too many blank lines (3) -590 589 | """ +595 595 | #!python +596 596 | +597 597 | +598 |- +599 598 | """This class docstring comes on line 5. +600 599 | It gives error E303: too many blank lines (3) +601 600 | """ -E30.py:600:5: E303 [*] Too many blank lines (2) +E30.py:611:5: E303 [*] Too many blank lines (2) | -600 | def b(self): +611 | def b(self): | ^^^ E303 -601 | pass -602 | # end +612 | pass +613 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -596 596 | def a(self): -597 597 | pass -598 598 | -599 |- -600 599 | def b(self): -601 600 | pass -602 601 | # end +607 607 | def a(self): +608 608 | pass +609 609 | +610 |- +611 610 | def b(self): +612 611 | pass +613 612 | # end -E30.py:610:5: E303 [*] Too many blank lines (2) +E30.py:621:5: E303 [*] Too many blank lines (2) | -610 | a = 2 +621 | a = 2 | ^ E303 -611 | # end +622 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -606 606 | if True: -607 607 | a = 1 -608 608 | -609 |- -610 609 | a = 2 -611 610 | # end -612 611 | +617 617 | if True: +618 618 | a = 1 +619 619 | +620 |- +621 620 | a = 2 +622 621 | # end +623 622 | -E30.py:618:5: E303 [*] Too many blank lines (2) +E30.py:629:5: E303 [*] Too many blank lines (2) | -618 | # comment +629 | # comment | ^^^^^^^^^ E303 | = help: Remove extraneous blank line(s) ℹ Safe fix -614 614 | # E303 -615 615 | class Test: -616 616 | -617 |- -618 617 | # comment -619 618 | -620 619 | +625 625 | # E303 +626 626 | class Test: +627 627 | +628 |- +629 628 | # comment +630 629 | +631 630 | -E30.py:621:5: E303 [*] Too many blank lines (2) +E30.py:632:5: E303 [*] Too many blank lines (2) | -621 | # another comment +632 | # another comment | ^^^^^^^^^^^^^^^^^ E303 -622 | -623 | def test(self): pass +633 | +634 | def test(self): pass | = help: Remove extraneous blank line(s) ℹ Safe fix -617 617 | -618 618 | # comment -619 619 | -620 |- -621 620 | # another comment -622 621 | -623 622 | def test(self): pass +628 628 | +629 629 | # comment +630 630 | +631 |- +632 631 | # another comment +633 632 | +634 633 | def test(self): pass + +E30.py:646:5: E303 [*] Too many blank lines (2) + | +646 | def b(self): + | ^^^ E303 +647 | pass +648 | # end + | + = help: Remove extraneous blank line(s) + +ℹ Safe fix +642 642 | +643 643 | # wrongly indented comment +644 644 | +645 |- +646 645 | def b(self): +647 646 | pass +648 647 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 60b9a67b3e9a5..2c320bae8996f 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,65 +1,65 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:630:1: E304 [*] blank lines found after function decorator +E30.py:654:1: E304 [*] blank lines found after function decorator | -628 | @decorator -629 | -630 | def function(): +652 | @decorator +653 | +654 | def function(): | ^^^ E304 -631 | pass -632 | # end +655 | pass +656 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -626 626 | -627 627 | # E304 -628 628 | @decorator -629 |- -630 629 | def function(): -631 630 | pass -632 631 | # end +650 650 | +651 651 | # E304 +652 652 | @decorator +653 |- +654 653 | def function(): +655 654 | pass +656 655 | # end -E30.py:639:1: E304 [*] blank lines found after function decorator +E30.py:663:1: E304 [*] blank lines found after function decorator | -638 | # comment E304 not expected -639 | def function(): +662 | # comment E304 not expected +663 | def function(): | ^^^ E304 -640 | pass -641 | # end +664 | pass +665 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -634 634 | -635 635 | # E304 -636 636 | @decorator -637 |- -638 637 | # comment E304 not expected -639 638 | def function(): -640 639 | pass +658 658 | +659 659 | # E304 +660 660 | @decorator +661 |- +662 661 | # comment E304 not expected +663 662 | def function(): +664 663 | pass -E30.py:651:1: E304 [*] blank lines found after function decorator +E30.py:675:1: E304 [*] blank lines found after function decorator | -650 | # second comment E304 not expected -651 | def function(): +674 | # second comment E304 not expected +675 | def function(): | ^^^ E304 -652 | pass -653 | # end +676 | pass +677 | # end | = help: Remove extraneous blank line(s) ℹ Safe fix -643 643 | -644 644 | # E304 -645 645 | @decorator -646 |- -647 646 | # comment E304 not expected -648 |- -649 |- -650 647 | # second comment E304 not expected -651 648 | def function(): -652 649 | pass +667 667 | +668 668 | # E304 +669 669 | @decorator +670 |- +671 670 | # comment E304 not expected +672 |- +673 |- +674 671 | # second comment E304 not expected +675 672 | def function(): +676 673 | pass diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 0981e4406115f..987bcb885f97b 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,102 +1,102 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:663:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:687:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -662 | # another comment -663 | fn() +686 | # another comment +687 | fn() | ^^ E305 -664 | # end +688 | # end | = help: Add missing blank line(s) ℹ Safe fix -660 660 | # comment -661 661 | -662 662 | # another comment - 663 |+ - 664 |+ -663 665 | fn() -664 666 | # end -665 667 | +684 684 | # comment +685 685 | +686 686 | # another comment + 687 |+ + 688 |+ +687 689 | fn() +688 690 | # end +689 691 | -E30.py:674:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:698:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -673 | # another comment -674 | a = 1 +697 | # another comment +698 | a = 1 | ^ E305 -675 | # end +699 | # end | = help: Add missing blank line(s) ℹ Safe fix -671 671 | # comment -672 672 | -673 673 | # another comment - 674 |+ - 675 |+ -674 676 | a = 1 -675 677 | # end -676 678 | +695 695 | # comment +696 696 | +697 697 | # another comment + 698 |+ + 699 |+ +698 700 | a = 1 +699 701 | # end +700 702 | -E30.py:686:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:710:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -684 | # another comment -685 | -686 | try: +708 | # another comment +709 | +710 | try: | ^^^ E305 -687 | fn() -688 | except Exception: +711 | fn() +712 | except Exception: | = help: Add missing blank line(s) ℹ Safe fix -683 683 | -684 684 | # another comment -685 685 | - 686 |+ -686 687 | try: -687 688 | fn() -688 689 | except Exception: +707 707 | +708 708 | # another comment +709 709 | + 710 |+ +710 711 | try: +711 712 | fn() +712 713 | except Exception: -E30.py:698:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:722:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -697 | # Two spaces before comments, too. -698 | if a(): +721 | # Two spaces before comments, too. +722 | if a(): | ^^ E305 -699 | a() -700 | # end +723 | a() +724 | # end | = help: Add missing blank line(s) ℹ Safe fix -695 695 | print() -696 696 | -697 697 | # Two spaces before comments, too. - 698 |+ - 699 |+ -698 700 | if a(): -699 701 | a() -700 702 | # end +719 719 | print() +720 720 | +721 721 | # Two spaces before comments, too. + 722 |+ + 723 |+ +722 724 | if a(): +723 725 | a() +724 726 | # end -E30.py:711:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:735:1: E305 [*] expected 2 blank lines after class or function definition, found (1) | -709 | blah, blah -710 | -711 | if __name__ == '__main__': +733 | blah, blah +734 | +735 | if __name__ == '__main__': | ^^ E305 -712 | main() -713 | # end +736 | main() +737 | # end | = help: Add missing blank line(s) ℹ Safe fix -708 708 | def main(): -709 709 | blah, blah -710 710 | - 711 |+ -711 712 | if __name__ == '__main__': -712 713 | main() -713 714 | # end +732 732 | def main(): +733 733 | blah, blah +734 734 | + 735 |+ +735 736 | if __name__ == '__main__': +736 737 | main() +737 738 | # end diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap index 1026bc1ea8d2b..c9a2629b06795 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E306_E30.py.snap @@ -1,223 +1,223 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:719:5: E306 [*] Expected 1 blank line before a nested definition, found 0 +E30.py:743:5: E306 [*] Expected 1 blank line before a nested definition, found 0 | -717 | def a(): -718 | x = 1 -719 | def b(): +741 | def a(): +742 | x = 1 +743 | def b(): | ^^^ E306 -720 | pass -721 | # end +744 | pass +745 | # end | = help: Add missing blank line ℹ Safe fix -716 716 | # E306:3:5 -717 717 | def a(): -718 718 | x = 1 - 719 |+ -719 720 | def b(): -720 721 | pass -721 722 | # end - -E30.py:727:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -725 | async def a(): -726 | x = 1 -727 | def b(): +740 740 | # E306:3:5 +741 741 | def a(): +742 742 | x = 1 + 743 |+ +743 744 | def b(): +744 745 | pass +745 746 | # end + +E30.py:751:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +749 | async def a(): +750 | x = 1 +751 | def b(): | ^^^ E306 -728 | pass -729 | # end +752 | pass +753 | # end | = help: Add missing blank line ℹ Safe fix -724 724 | #: E306:3:5 -725 725 | async def a(): -726 726 | x = 1 - 727 |+ -727 728 | def b(): -728 729 | pass -729 730 | # end - -E30.py:735:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -733 | def a(): -734 | x = 2 -735 | def b(): +748 748 | #: E306:3:5 +749 749 | async def a(): +750 750 | x = 1 + 751 |+ +751 752 | def b(): +752 753 | pass +753 754 | # end + +E30.py:759:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +757 | def a(): +758 | x = 2 +759 | def b(): | ^^^ E306 -736 | x = 1 -737 | def c(): +760 | x = 1 +761 | def c(): | = help: Add missing blank line ℹ Safe fix -732 732 | #: E306:3:5 E306:5:9 -733 733 | def a(): -734 734 | x = 2 - 735 |+ -735 736 | def b(): -736 737 | x = 1 -737 738 | def c(): - -E30.py:737:9: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -735 | def b(): -736 | x = 1 -737 | def c(): +756 756 | #: E306:3:5 E306:5:9 +757 757 | def a(): +758 758 | x = 2 + 759 |+ +759 760 | def b(): +760 761 | x = 1 +761 762 | def c(): + +E30.py:761:9: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +759 | def b(): +760 | x = 1 +761 | def c(): | ^^^ E306 -738 | pass -739 | # end +762 | pass +763 | # end | = help: Add missing blank line ℹ Safe fix -734 734 | x = 2 -735 735 | def b(): -736 736 | x = 1 - 737 |+ -737 738 | def c(): -738 739 | pass -739 740 | # end - -E30.py:745:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -743 | def a(): -744 | x = 1 -745 | class C: +758 758 | x = 2 +759 759 | def b(): +760 760 | x = 1 + 761 |+ +761 762 | def c(): +762 763 | pass +763 764 | # end + +E30.py:769:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +767 | def a(): +768 | x = 1 +769 | class C: | ^^^^^ E306 -746 | pass -747 | x = 2 +770 | pass +771 | x = 2 | = help: Add missing blank line ℹ Safe fix -742 742 | # E306:3:5 E306:6:5 -743 743 | def a(): -744 744 | x = 1 - 745 |+ -745 746 | class C: -746 747 | pass -747 748 | x = 2 - -E30.py:748:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -746 | pass -747 | x = 2 -748 | def b(): +766 766 | # E306:3:5 E306:6:5 +767 767 | def a(): +768 768 | x = 1 + 769 |+ +769 770 | class C: +770 771 | pass +771 772 | x = 2 + +E30.py:772:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +770 | pass +771 | x = 2 +772 | def b(): | ^^^ E306 -749 | pass -750 | # end +773 | pass +774 | # end | = help: Add missing blank line ℹ Safe fix -745 745 | class C: -746 746 | pass -747 747 | x = 2 - 748 |+ -748 749 | def b(): -749 750 | pass -750 751 | # end - -E30.py:757:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -755 | def bar(): -756 | pass -757 | def baz(): pass +769 769 | class C: +770 770 | pass +771 771 | x = 2 + 772 |+ +772 773 | def b(): +773 774 | pass +774 775 | # end + +E30.py:781:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +779 | def bar(): +780 | pass +781 | def baz(): pass | ^^^ E306 -758 | # end +782 | # end | = help: Add missing blank line ℹ Safe fix -754 754 | def foo(): -755 755 | def bar(): -756 756 | pass - 757 |+ -757 758 | def baz(): pass -758 759 | # end -759 760 | - -E30.py:764:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -762 | def foo(): -763 | def bar(): pass -764 | def baz(): +778 778 | def foo(): +779 779 | def bar(): +780 780 | pass + 781 |+ +781 782 | def baz(): pass +782 783 | # end +783 784 | + +E30.py:788:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +786 | def foo(): +787 | def bar(): pass +788 | def baz(): | ^^^ E306 -765 | pass -766 | # end +789 | pass +790 | # end | = help: Add missing blank line ℹ Safe fix -761 761 | # E306:3:5 -762 762 | def foo(): -763 763 | def bar(): pass - 764 |+ -764 765 | def baz(): -765 766 | pass -766 767 | # end - -E30.py:772:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -770 | def a(): -771 | x = 2 -772 | @decorator +785 785 | # E306:3:5 +786 786 | def foo(): +787 787 | def bar(): pass + 788 |+ +788 789 | def baz(): +789 790 | pass +790 791 | # end + +E30.py:796:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +794 | def a(): +795 | x = 2 +796 | @decorator | ^ E306 -773 | def b(): -774 | pass +797 | def b(): +798 | pass | = help: Add missing blank line ℹ Safe fix -769 769 | # E306 -770 770 | def a(): -771 771 | x = 2 - 772 |+ -772 773 | @decorator -773 774 | def b(): -774 775 | pass - -E30.py:781:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -779 | def a(): -780 | x = 2 -781 | @decorator +793 793 | # E306 +794 794 | def a(): +795 795 | x = 2 + 796 |+ +796 797 | @decorator +797 798 | def b(): +798 799 | pass + +E30.py:805:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +803 | def a(): +804 | x = 2 +805 | @decorator | ^ E306 -782 | async def b(): -783 | pass +806 | async def b(): +807 | pass | = help: Add missing blank line ℹ Safe fix -778 778 | # E306 -779 779 | def a(): -780 780 | x = 2 - 781 |+ -781 782 | @decorator -782 783 | async def b(): -783 784 | pass - -E30.py:790:5: E306 [*] Expected 1 blank line before a nested definition, found 0 - | -788 | def a(): -789 | x = 2 -790 | async def b(): +802 802 | # E306 +803 803 | def a(): +804 804 | x = 2 + 805 |+ +805 806 | @decorator +806 807 | async def b(): +807 808 | pass + +E30.py:814:5: E306 [*] Expected 1 blank line before a nested definition, found 0 + | +812 | def a(): +813 | x = 2 +814 | async def b(): | ^^^^^ E306 -791 | pass -792 | # end +815 | pass +816 | # end | = help: Add missing blank line ℹ Safe fix -787 787 | # E306 -788 788 | def a(): -789 789 | x = 2 - 790 |+ -790 791 | async def b(): -791 792 | pass -792 793 | # end +811 811 | # E306 +812 812 | def a(): +813 813 | x = 2 + 814 |+ +814 815 | async def b(): +815 816 | pass +816 817 | # end From 3d5cc8455cb239e009f59d4ff4fb0da95126dea1 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 7 Feb 2024 08:37:54 +0900 Subject: [PATCH 117/122] fix E301 docstring. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index aa2ad632c28eb..79f739f4e64ee 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -30,9 +30,7 @@ const BLANK_LINES_METHOD_LEVEL: u32 = 1; /// Checks for missing blank lines between methods of a class. /// /// ## Why is this bad? -/// PEP 8 recommends the use of blank lines as follows: -/// - Two blank lines are expected between functions and classes -/// - One blank line is expected between methods of a class. +/// PEP 8 recommends exactly one blank line between methods of a class. /// /// ## Example /// ```python From 0596eda0692dfc616184c4a46dd908ba2d09b5b5 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 7 Feb 2024 08:42:49 +0900 Subject: [PATCH 118/122] fix E302 docstring. --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 79f739f4e64ee..b8192c9115805 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -77,9 +77,7 @@ impl AlwaysFixableViolation for BlankLineBetweenMethods { /// Checks for missing blank lines between top level functions and classes. /// /// ## Why is this bad? -/// PEP 8 recommends the use of blank lines as follows: -/// - Two blank lines are expected between functions and classes -/// - One blank line is expected between methods of a class. +/// PEP 8 recommends exactly two blank lines between top level functions and classes. /// /// ## Example /// ```python From d188e200ecaca6d1abf1883d753012f1b6cfbfc6 Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 7 Feb 2024 08:44:20 +0900 Subject: [PATCH 119/122] Fix E301's fix message --- crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index b8192c9115805..e4d14762b34ca 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -69,7 +69,7 @@ impl AlwaysFixableViolation for BlankLineBetweenMethods { } fn fix_title(&self) -> String { - "Add missing blank line(s)".to_string() + "Add missing blank line".to_string() } } From e89c20f86c5c63a8ad779bd020bbe74b549117de Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 7 Feb 2024 08:48:59 +0900 Subject: [PATCH 120/122] Fix E304's docstring. --- .../ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index e4d14762b34ca..cfafa15abe353 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -172,12 +172,10 @@ impl AlwaysFixableViolation for TooManyBlankLines { } /// ## What it does -/// Checks for missing blank line after function decorator. +/// Checks for extraneous blank line(s) after function decorator. /// /// ## Why is this bad? -/// PEP 8 recommends the use of blank lines as follows: -/// - Two blank lines are expected between functions and classes -/// - One blank line is expected between methods of a class. +/// There should be no blank lines between a decorator and the object it is decorating. /// /// ## Example /// ```python From a28f916c8191b759fcb83f72b7724d265903915c Mon Sep 17 00:00:00 2001 From: Hoel Bagard Date: Wed, 7 Feb 2024 08:50:32 +0900 Subject: [PATCH 121/122] Fix error message capitalization. --- .../src/rules/pycodestyle/rules/blank_lines.rs | 4 ++-- ...linter__rules__pycodestyle__tests__E301_E30.py.snap | 4 ++-- ...linter__rules__pycodestyle__tests__E304_E30.py.snap | 6 +++--- ...linter__rules__pycodestyle__tests__E305_E30.py.snap | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index cfafa15abe353..7e4e76a0f1250 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -205,7 +205,7 @@ pub struct BlankLineAfterDecorator; impl AlwaysFixableViolation for BlankLineAfterDecorator { #[derive_message_formats] fn message(&self) -> String { - format!("blank lines found after function decorator") + format!("Blank lines found after function decorator") } fn fix_title(&self) -> String { @@ -251,7 +251,7 @@ impl AlwaysFixableViolation for BlankLinesAfterFunctionOrClass { let BlankLinesAfterFunctionOrClass { actual_blank_lines: blank_lines, } = self; - format!("expected 2 blank lines after class or function definition, found ({blank_lines})") + format!("Expected 2 blank lines after class or function definition, found ({blank_lines})") } fn fix_title(&self) -> String { diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap index 5e3e0713fc108..483170ced3def 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E301_E30.py.snap @@ -10,7 +10,7 @@ E30.py:444:5: E301 [*] Expected 1 blank line, found 0 445 | pass 446 | # end | - = help: Add missing blank line(s) + = help: Add missing blank line ℹ Safe fix 441 441 | @@ -30,7 +30,7 @@ E30.py:455:5: E301 [*] Expected 1 blank line, found 0 456 | pass 457 | # end | - = help: Add missing blank line(s) + = help: Add missing blank line ℹ Safe fix 451 451 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 2c320bae8996f..75b28a80529d5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:654:1: E304 [*] blank lines found after function decorator +E30.py:654:1: E304 [*] Blank lines found after function decorator | 652 | @decorator 653 | @@ -21,7 +21,7 @@ E30.py:654:1: E304 [*] blank lines found after function decorator 655 654 | pass 656 655 | # end -E30.py:663:1: E304 [*] blank lines found after function decorator +E30.py:663:1: E304 [*] Blank lines found after function decorator | 662 | # comment E304 not expected 663 | def function(): @@ -40,7 +40,7 @@ E30.py:663:1: E304 [*] blank lines found after function decorator 663 662 | def function(): 664 663 | pass -E30.py:675:1: E304 [*] blank lines found after function decorator +E30.py:675:1: E304 [*] Blank lines found after function decorator | 674 | # second comment E304 not expected 675 | def function(): diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap index 987bcb885f97b..4addcca185964 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E305_E30.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:687:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:687:1: E305 [*] Expected 2 blank lines after class or function definition, found (1) | 686 | # another comment 687 | fn() @@ -20,7 +20,7 @@ E30.py:687:1: E305 [*] expected 2 blank lines after class or function definition 688 690 | # end 689 691 | -E30.py:698:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:698:1: E305 [*] Expected 2 blank lines after class or function definition, found (1) | 697 | # another comment 698 | a = 1 @@ -39,7 +39,7 @@ E30.py:698:1: E305 [*] expected 2 blank lines after class or function definition 699 701 | # end 700 702 | -E30.py:710:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:710:1: E305 [*] Expected 2 blank lines after class or function definition, found (1) | 708 | # another comment 709 | @@ -59,7 +59,7 @@ E30.py:710:1: E305 [*] expected 2 blank lines after class or function definition 711 712 | fn() 712 713 | except Exception: -E30.py:722:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:722:1: E305 [*] Expected 2 blank lines after class or function definition, found (1) | 721 | # Two spaces before comments, too. 722 | if a(): @@ -79,7 +79,7 @@ E30.py:722:1: E305 [*] expected 2 blank lines after class or function definition 723 725 | a() 724 726 | # end -E30.py:735:1: E305 [*] expected 2 blank lines after class or function definition, found (1) +E30.py:735:1: E305 [*] Expected 2 blank lines after class or function definition, found (1) | 733 | blah, blah 734 | From aea3493e616fd82ab56197cb5575a08f10f670c4 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Thu, 8 Feb 2024 13:29:21 -0500 Subject: [PATCH 122/122] Remove unnecessary state variables --- .../rules/pycodestyle/rules/blank_lines.rs | 61 ++++++++----------- ...ules__pycodestyle__tests__E304_E30.py.snap | 6 +- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs index 7e4e76a0f1250..cd3e23b5024d7 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/blank_lines.rs @@ -55,17 +55,12 @@ const BLANK_LINES_METHOD_LEVEL: u32 = 1; /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E301.html) #[violation] -pub struct BlankLineBetweenMethods { - actual_blank_lines: u32, -} +pub struct BlankLineBetweenMethods; impl AlwaysFixableViolation for BlankLineBetweenMethods { #[derive_message_formats] fn message(&self) -> String { - let BlankLineBetweenMethods { - actual_blank_lines: nb_blank_lines, - } = self; - format!("Expected {BLANK_LINES_METHOD_LEVEL:?} blank line, found {nb_blank_lines}") + format!("Expected {BLANK_LINES_METHOD_LEVEL:?} blank line, found 0") } fn fix_title(&self) -> String { @@ -172,7 +167,7 @@ impl AlwaysFixableViolation for TooManyBlankLines { } /// ## What it does -/// Checks for extraneous blank line(s) after function decorator. +/// Checks for extraneous blank line(s) after function decorators. /// /// ## Why is this bad? /// There should be no blank lines between a decorator and the object it is decorating. @@ -200,12 +195,17 @@ impl AlwaysFixableViolation for TooManyBlankLines { /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E304.html) #[violation] -pub struct BlankLineAfterDecorator; +pub struct BlankLineAfterDecorator { + actual_blank_lines: u32, +} impl AlwaysFixableViolation for BlankLineAfterDecorator { #[derive_message_formats] fn message(&self) -> String { - format!("Blank lines found after function decorator") + format!( + "Blank lines found after function decorator ({lines})", + lines = self.actual_blank_lines + ) } fn fix_title(&self) -> String { @@ -214,10 +214,10 @@ impl AlwaysFixableViolation for BlankLineAfterDecorator { } /// ## What it does -/// Checks for missing blank lines after end of function or class. +/// Checks for missing blank lines after the end of function or class. /// /// ## Why is this bad? -/// PEP 8 recommends the using blank lines as following: +/// PEP 8 recommends using blank lines as following: /// - Two blank lines are expected between functions and classes /// - One blank line is expected between methods of a class. /// @@ -260,10 +260,10 @@ impl AlwaysFixableViolation for BlankLinesAfterFunctionOrClass { } /// ## What it does -/// Checks for for 1 blank line between nested functions/classes definitions. +/// Checks for 1 blank line between nested function or class definitions. /// /// ## Why is this bad? -/// PEP 8 recommends the using blank lines as following: +/// PEP 8 recommends using blank lines as following: /// - Two blank lines are expected between functions and classes /// - One blank line is expected between methods of a class. /// @@ -290,17 +290,12 @@ impl AlwaysFixableViolation for BlankLinesAfterFunctionOrClass { /// - [PEP 8](https://peps.python.org/pep-0008/#blank-lines) /// - [Flake 8 rule](https://www.flake8rules.com/rules/E306.html) #[violation] -pub struct BlankLinesBeforeNestedDefinition { - actual_blank_lines: u32, -} +pub struct BlankLinesBeforeNestedDefinition; impl AlwaysFixableViolation for BlankLinesBeforeNestedDefinition { #[derive_message_formats] fn message(&self) -> String { - let BlankLinesBeforeNestedDefinition { - actual_blank_lines: blank_lines, - } = self; - format!("Expected 1 blank line before a nested definition, found {blank_lines}") + format!("Expected 1 blank line before a nested definition, found 0") } fn fix_title(&self) -> String { @@ -673,12 +668,8 @@ impl BlankLinesChecker { && prev_indent_length.is_some_and(|prev_indent_length| prev_indent_length >= line.indent_length) { // E301 - let mut diagnostic = Diagnostic::new( - BlankLineBetweenMethods { - actual_blank_lines: 0, - }, - line.first_token_range, - ); + let mut diagnostic = + Diagnostic::new(BlankLineBetweenMethods, line.first_token_range); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().to_string(), locator.line_start(self.last_non_comment_line_end), @@ -750,8 +741,12 @@ impl BlankLinesChecker { && line.preceding_blank_lines > 0 { // E304 - let mut diagnostic = - Diagnostic::new(BlankLineAfterDecorator, line.first_token_range); + let mut diagnostic = Diagnostic::new( + BlankLineAfterDecorator { + actual_blank_lines: line.preceding_blank_lines.count(), + }, + line.first_token_range, + ); // Get all the lines between the last decorator line (included) and the current line (included). // Then remove all blank lines. @@ -827,12 +822,8 @@ impl BlankLinesChecker { && !(matches!(self.follows, Follows::Def) && line.last_token != TokenKind::Colon) { // E306 - let mut diagnostic = Diagnostic::new( - BlankLinesBeforeNestedDefinition { - actual_blank_lines: 0, - }, - line.first_token_range, - ); + let mut diagnostic = + Diagnostic::new(BlankLinesBeforeNestedDefinition, line.first_token_range); diagnostic.set_fix(Fix::safe_edit(Edit::insertion( stylist.line_ending().to_string(), diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap index 75b28a80529d5..adf95ea1bc540 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E304_E30.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/pycodestyle/mod.rs --- -E30.py:654:1: E304 [*] Blank lines found after function decorator +E30.py:654:1: E304 [*] Blank lines found after function decorator (1) | 652 | @decorator 653 | @@ -21,7 +21,7 @@ E30.py:654:1: E304 [*] Blank lines found after function decorator 655 654 | pass 656 655 | # end -E30.py:663:1: E304 [*] Blank lines found after function decorator +E30.py:663:1: E304 [*] Blank lines found after function decorator (1) | 662 | # comment E304 not expected 663 | def function(): @@ -40,7 +40,7 @@ E30.py:663:1: E304 [*] Blank lines found after function decorator 663 662 | def function(): 664 663 | pass -E30.py:675:1: E304 [*] Blank lines found after function decorator +E30.py:675:1: E304 [*] Blank lines found after function decorator (2) | 674 | # second comment E304 not expected 675 | def function():