diff --git a/crates/ruff_linter/resources/test/fixtures/pycodestyle/E2_syntax_error.py b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E2_syntax_error.py new file mode 100644 index 0000000000000..34c9579d58cc2 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pycodestyle/E2_syntax_error.py @@ -0,0 +1 @@ +a = (1 or) diff --git a/crates/ruff_linter/src/rules/pycodestyle/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/mod.rs index a6d850f862aef..156fde4924b48 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/mod.rs @@ -148,6 +148,23 @@ mod tests { Ok(()) } + /// Tests the compatibility of E2 rules (E202, E225 and E275) on syntactically incorrect code. + #[test] + fn white_space_syntax_error_compatibility() -> Result<()> { + let diagnostics = test_path( + Path::new("pycodestyle").join("E2_syntax_error.py"), + &settings::LinterSettings { + ..settings::LinterSettings::for_rules([ + Rule::MissingWhitespaceAroundOperator, + Rule::MissingWhitespaceAfterKeyword, + Rule::WhitespaceBeforeCloseBracket, + ]) + }, + )?; + assert_messages!(diagnostics); + Ok(()) + } + #[test_case(Rule::BlankLineBetweenMethods, Path::new("E30.py"))] #[test_case(Rule::BlankLinesTopLevel, Path::new("E30.py"))] #[test_case(Rule::TooManyBlankLines, Path::new("E30.py"))] diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/missing_whitespace_after_keyword.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/missing_whitespace_after_keyword.rs index 7aa8cfc24764a..296d9514bda6e 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/missing_whitespace_after_keyword.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/missing_whitespace_after_keyword.rs @@ -59,7 +59,13 @@ pub(crate) fn missing_whitespace_after_keyword( || tok0_kind == TokenKind::Yield && tok1_kind == TokenKind::Rpar || matches!( tok1_kind, - TokenKind::Colon | TokenKind::Newline | TokenKind::NonLogicalNewline + TokenKind::Colon + | TokenKind::Newline + | TokenKind::NonLogicalNewline + // In the event of a syntax error, do not attempt to add a whitespace. + | TokenKind::Rpar + | TokenKind::Rsqb + | TokenKind::Rbrace )) && tok0.end() == tok1.start() { 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 c5432802ccd26..fd182648caec3 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 @@ -211,6 +211,21 @@ pub(crate) fn missing_whitespace_around_operator( } else { NeedsSpace::No } + } else if tokens.peek().is_some_and(|token| { + matches!( + token.kind(), + TokenKind::Rpar | TokenKind::Rsqb | TokenKind::Rbrace + ) + }) { + // There should not be a closing bracket directly after a token, as it is a syntax + // error. For example: + // ``` + // 1+) + // ``` + // + // However, allow it in order to prevent entering an infinite loop in which E225 adds a + // space only for E202 to remove it. + NeedsSpace::No } else if is_whitespace_needed(kind) { NeedsSpace::Yes } else { diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__white_space_syntax_error_compatibility.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__white_space_syntax_error_compatibility.snap new file mode 100644 index 0000000000000..6dcc4546f11f9 --- /dev/null +++ b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__white_space_syntax_error_compatibility.snap @@ -0,0 +1,4 @@ +--- +source: crates/ruff_linter/src/rules/pycodestyle/mod.rs +--- +