Skip to content

Commit

Permalink
[pycodestyle] Avoid false positives and negatives related to type p…
Browse files Browse the repository at this point in the history
…arameter default syntax (`E225`, `E251`) (#15214)
  • Loading branch information
InSyncWithFoo authored Jan 1, 2025
1 parent 79682a2 commit af95f6b
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 11 deletions.
5 changes: 5 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pycodestyle/E22.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,8 @@ def squares(n):

if a == 1:
print(a)


# No E225: `=` is not an operator in this case
def _[T: str=None](): ...
def _(t: str=None): ...
4 changes: 4 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pycodestyle/E25.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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()
Expand All @@ -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;
}
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,8 @@ struct Line {
enum DefinitionState {
InClass(TypeParamsState),
InFunction(TypeParamsState),
NotInClassOrFunction,
InTypeAlias(TypeParamsState),
NotInDefinition,
}

impl DefinitionState {
Expand All @@ -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 {
Expand All @@ -507,8 +509,10 @@ impl DefinitionState {

const fn type_params_state(self) -> Option<TypeParamsState> {
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,
}
}

Expand All @@ -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() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

0 comments on commit af95f6b

Please sign in to comment.