Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pycodestyle] Whitespace after decorator (E204) #12140

Merged
merged 12 commits into from
Jul 4, 2024
27 changes: 27 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pycodestyle/E204.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
def foo(fun):
def wrapper():
print('before')
fun()
print('after')
return wrapper

# No error
@foo
def bar():
print('bar')

# E204
@ foo
def baz():
print('baz')

class Test:
# No error
@foo
def bar(self):
print('bar')

# E204
@ foo
def baz(self):
print('baz')
6 changes: 6 additions & 0 deletions crates/ruff_linter/src/checkers/ast/analyze/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::UnusedAsync) {
ruff::rules::unused_async(checker, function_def);
}
if checker.enabled(Rule::WhitespaceAfterDecorator) {
pycodestyle::rules::whitespace_after_decorator(checker, decorator_list);
}
}
Stmt::Return(_) => {
if checker.enabled(Rule::ReturnOutsideFunction) {
Expand Down Expand Up @@ -531,6 +534,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::MetaClassABCMeta) {
refurb::rules::metaclass_abcmeta(checker, class_def);
}
if checker.enabled(Rule::WhitespaceAfterDecorator) {
pycodestyle::rules::whitespace_after_decorator(checker, decorator_list);
}
}
Stmt::Import(ast::StmtImport { names, range: _ }) => {
if checker.enabled(Rule::MultipleImportsOnOneLine) {
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pycodestyle, "E202") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::WhitespaceBeforeCloseBracket),
#[allow(deprecated)]
(Pycodestyle, "E203") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::WhitespaceBeforePunctuation),
(Pycodestyle, "E204") => (RuleGroup::Preview, rules::pycodestyle::rules::WhitespaceAfterDecorator),
#[allow(deprecated)]
(Pycodestyle, "E211") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::WhitespaceBeforeParameters),
#[allow(deprecated)]
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/rules/pycodestyle/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub(crate) use tab_indentation::*;
pub(crate) use too_many_newlines_at_end_of_file::*;
pub(crate) use trailing_whitespace::*;
pub(crate) use type_comparison::*;
pub(crate) use whitespace_after_decorator::*;

mod ambiguous_class_name;
mod ambiguous_function_name;
Expand All @@ -43,3 +44,4 @@ mod tab_indentation;
mod too_many_newlines_at_end_of_file;
mod trailing_whitespace;
mod type_comparison;
mod whitespace_after_decorator;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::Decorator;
use ruff_python_trivia::is_python_whitespace;
use ruff_text_size::{TextLen, TextRange};

use crate::checkers::ast::Checker;

/// ## What it does
/// Checks for whitespace after a decorator.
///
/// ## Why is this bad?
/// Whitespace after a decorator is not PEP8 compliant.
///
/// ## Example
///
/// ```python
/// @ decorator
/// def foo():
/// pass
/// ```
/// Use instead:
/// ``` python
/// @decorator
/// def foo():
/// pass
/// ```

#[violation]
pub struct WhitespaceAfterDecorator;

impl AlwaysFixableViolation for WhitespaceAfterDecorator {
#[derive_message_formats]
fn message(&self) -> String {
format!("Whitespace after decorator")
}

fn fix_title(&self) -> String {
"Remove whitespace after decorator".to_string()
}
}

/// E204
pub(crate) fn whitespace_after_decorator(checker: &mut Checker, decorator_list: &[Decorator]) {
// Get the locator from the checker
let locator = checker.locator();

// Iterate over the list of decorators
for decorator in decorator_list {
// Obtain the text of the decorator using lactor.slice(decorator)
let decorator_text = locator.slice(decorator);

// Get the text after the @ symbol
let after_at = &decorator_text[1..];

// Check if there is a whitespace after the @ symbol by using is_python_whitespace
if is_python_whitespace(after_at.chars().next().unwrap()) {
let range = TextRange::empty(locator.contents().text_len());
checker
.diagnostics
.push(Diagnostic::new(WhitespaceAfterDecorator, range));
}
}
}
1 change: 1 addition & 0 deletions ruff.schema.json

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