diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/repeated_equality_comparison.py b/crates/ruff_linter/resources/test/fixtures/pylint/repeated_equality_comparison.py index 6c3f694ba87cd..8eeec8bdafbbc 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/repeated_equality_comparison.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/repeated_equality_comparison.py @@ -51,3 +51,7 @@ foo[0] == "a" or foo[0] == "b" # Subscripts. foo() == "a" or foo() == "b" # Calls. + +import sys + +sys.platform == "win32" or sys.platform == "emscripten" # sys attributes diff --git a/crates/ruff_linter/src/rules/pylint/rules/repeated_equality_comparison.rs b/crates/ruff_linter/src/rules/pylint/rules/repeated_equality_comparison.rs index b4c5a792256d0..25f36d63a1bd9 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/repeated_equality_comparison.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/repeated_equality_comparison.rs @@ -9,7 +9,9 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::hashable::HashableExpr; +use ruff_python_ast::helpers::any_over_expr; use ruff_python_ast::{self as ast, BoolOp, CmpOp, Expr}; +use ruff_python_semantic::SemanticModel; use ruff_source_file::Locator; use ruff_text_size::{Ranged, TextRange, TextSize}; @@ -74,7 +76,7 @@ pub(crate) fn repeated_equality_comparison(checker: &mut Checker, bool_op: &ast: if bool_op .values .iter() - .any(|value| !is_allowed_value(bool_op.op, value)) + .any(|value| !is_allowed_value(bool_op.op, value, checker.semantic())) { return; } @@ -157,7 +159,7 @@ pub(crate) fn repeated_equality_comparison(checker: &mut Checker, bool_op: &ast: /// Return `true` if the given expression is compatible with a membership test. /// E.g., `==` operators can be joined with `or` and `!=` operators can be /// joined with `and`. -fn is_allowed_value(bool_op: BoolOp, value: &Expr) -> bool { +fn is_allowed_value(bool_op: BoolOp, value: &Expr, semantic: &SemanticModel) -> bool { let Expr::Compare(ast::ExprCompare { left, ops, @@ -196,6 +198,16 @@ fn is_allowed_value(bool_op: BoolOp, value: &Expr) -> bool { return false; } + // Ignore `sys.version_info` and `sys.platform` comparisons, which are only + // respected by type checkers when enforced via equality. + if any_over_expr(value, &|expr| { + semantic.resolve_call_path(expr).is_some_and(|call_path| { + matches!(call_path.as_slice(), ["sys", "version_info" | "platform"]) + }) + }) { + return false; + } + true }