From af95f6b5774b42d27d9b27eda269764f79f15f28 Mon Sep 17 00:00:00 2001 From: InSync Date: Wed, 1 Jan 2025 17:28:25 +0700 Subject: [PATCH] [`pycodestyle`] Avoid false positives and negatives related to type parameter default syntax (`E225`, `E251`) (#15214) --- .../test/fixtures/pycodestyle/E22.py | 5 +++++ .../test/fixtures/pycodestyle/E25.py | 4 ++++ .../missing_whitespace_around_operator.rs | 10 +++++++-- .../pycodestyle/rules/logical_lines/mod.rs | 22 +++++++++++-------- ...ules__pycodestyle__tests__E221_E22.py.snap | 2 ++ ...ules__pycodestyle__tests__E222_E22.py.snap | 2 ++ 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E22.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E22.py index d20bf42781..dae9d26b1e 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E22.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E22.py @@ -183,3 +183,8 @@ if i == -1: if a == 1: print(a) + + +# No E225: `=` is not an operator in this case +def _[T: str=None](): ... +def _(t: str=None): ... diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E25.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E25.py index 90f112cfc2..5ce8b9cfaa 100644 --- a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E25.py +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E25.py @@ -75,3 +75,7 @@ def pep_696_good[A = int, B: object = str, C:object = memoryview](): class PEP696Good[A = int, B: object = str, C:object = memoryview]: def pep_696_good_method[A = int, B: object = str, C:object = memoryview](self): pass + + +# https://github.com/astral-sh/ruff/issues/15202 +type Coro[T: object = Any] = Coroutine[None, None, T] diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/missing_whitespace_around_operator.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/missing_whitespace_around_operator.rs index 73edb01e41..064d7fabd5 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/missing_whitespace_around_operator.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/missing_whitespace_around_operator.rs @@ -5,7 +5,7 @@ use ruff_text_size::Ranged; use crate::checkers::logical_lines::LogicalLinesContext; use crate::rules::pycodestyle::helpers::is_non_logical_token; -use crate::rules::pycodestyle::rules::logical_lines::LogicalLine; +use crate::rules::pycodestyle::rules::logical_lines::{DefinitionState, LogicalLine}; /// ## What it does /// Checks for missing whitespace around all operators. @@ -146,6 +146,7 @@ pub(crate) fn missing_whitespace_around_operator( line: &LogicalLine, context: &mut LogicalLinesContext, ) { + let mut definition_state = DefinitionState::from_tokens(line.tokens()); let mut tokens = line.tokens().iter().peekable(); let first_token = tokens .by_ref() @@ -162,6 +163,8 @@ pub(crate) fn missing_whitespace_around_operator( while let Some(token) = tokens.next() { let kind = token.kind(); + definition_state.visit_token_kind(kind); + if is_non_logical_token(kind) { continue; } @@ -174,8 +177,11 @@ pub(crate) fn missing_whitespace_around_operator( _ => {} }; - let needs_space = if kind == TokenKind::Equal && (parens > 0 || fstrings > 0) { + let needs_space = if kind == TokenKind::Equal + && (parens > 0 || fstrings > 0 || definition_state.in_type_params()) + { // Allow keyword args, defaults: foo(bar=None) and f-strings: f'{foo=}' + // Also ignore `foo[T=int]`, which is handled by E251. NeedsSpace::No } else if kind == TokenKind::Slash { // Tolerate the "/" operator in function definition 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 777aa980b3..9c4e1c0e58 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 @@ -480,7 +480,8 @@ struct Line { enum DefinitionState { InClass(TypeParamsState), InFunction(TypeParamsState), - NotInClassOrFunction, + InTypeAlias(TypeParamsState), + NotInDefinition, } impl DefinitionState { @@ -494,11 +495,12 @@ impl DefinitionState { TokenKind::Async if matches!(token_kinds.next(), Some(TokenKind::Def)) => { Self::InFunction(TypeParamsState::default()) } - _ => Self::NotInClassOrFunction, + TokenKind::Type => Self::InTypeAlias(TypeParamsState::default()), + _ => Self::NotInDefinition, }; return state; } - Self::NotInClassOrFunction + Self::NotInDefinition } const fn in_function_definition(self) -> bool { @@ -507,8 +509,10 @@ impl DefinitionState { const fn type_params_state(self) -> Option { match self { - Self::InClass(state) | Self::InFunction(state) => Some(state), - Self::NotInClassOrFunction => None, + Self::InClass(state) | Self::InFunction(state) | Self::InTypeAlias(state) => { + Some(state) + } + Self::NotInDefinition => None, } } @@ -521,10 +525,10 @@ impl DefinitionState { fn visit_token_kind(&mut self, token_kind: TokenKind) { let type_params_state_mut = match self { - Self::InClass(type_params_state) | Self::InFunction(type_params_state) => { - type_params_state - } - Self::NotInClassOrFunction => return, + Self::InClass(type_params_state) + | Self::InFunction(type_params_state) + | Self::InTypeAlias(type_params_state) => type_params_state, + Self::NotInDefinition => return, }; match token_kind { TokenKind::Lpar if type_params_state_mut.before_type_params() => { diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E221_E22.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E221_E22.py.snap index 8dd9b259ed..5a5cd4fad1 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E221_E22.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E221_E22.py.snap @@ -186,3 +186,5 @@ E22.py:184:5: E221 [*] Multiple spaces before operator 184 |-if a == 1: 184 |+if a == 1: 185 185 | print(a) +186 186 | +187 187 | diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E222_E22.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E222_E22.py.snap index 4b51eba90f..9fd516cd3e 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E222_E22.py.snap +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__E222_E22.py.snap @@ -123,3 +123,5 @@ E22.py:184:9: E222 [*] Multiple spaces after operator 184 |-if a == 1: 184 |+if a == 1: 185 185 | print(a) +186 186 | +187 187 |