diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE796.py b/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE796.py index a0eeb627d0..83985bf247 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE796.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE796.py @@ -78,3 +78,9 @@ class FakeEnum11(enum.Enum): A = cast(SomeType, ...) B = cast(SomeType, ...) # PIE796 C = cast(SomeType, ...) # PIE796 + + +class FakeEnum12(enum.Enum): + A = enum.auto(), 0 + B = enum.auto(), 1 + C = enum.auto(), 0 diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs index bf967c3297..5a8783155b 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/non_unique_enums.rs @@ -1,3 +1,4 @@ +use ruff_python_semantic::SemanticModel; use rustc_hash::FxHashSet; use ruff_diagnostics::Diagnostic; @@ -75,17 +76,15 @@ pub(crate) fn non_unique_enums(checker: &Checker, parent: &Stmt, body: &[Stmt]) continue; }; - if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() { - if checker - .semantic() - .resolve_qualified_name(func) - .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["enum", "auto"])) - { + if is_call_to_enum_auto(semantic, value) { + continue; + } else if let Expr::Tuple(ast::ExprTuple { elts, .. }) = value.as_ref() { + if elts.iter().any(|elt| is_call_to_enum_auto(semantic, elt)) { continue; } } - if checker.source_type.is_stub() && member_has_unknown_value(checker, value) { + if checker.source_type.is_stub() && member_has_unknown_value(semantic, value) { continue; } @@ -103,16 +102,24 @@ pub(crate) fn non_unique_enums(checker: &Checker, parent: &Stmt, body: &[Stmt]) } } +fn is_call_to_enum_auto(semantic: &SemanticModel, expr: &Expr) -> bool { + expr.as_call_expr().is_some_and(|call| { + semantic + .resolve_qualified_name(&call.func) + .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["enum", "auto"])) + }) +} + /// Whether the value is a bare ellipsis literal (`A = ...`) /// or a casted one (`A = cast(SomeType, ...)`). -fn member_has_unknown_value(checker: &Checker, expr: &Expr) -> bool { +fn member_has_unknown_value(semantic: &SemanticModel, expr: &Expr) -> bool { match expr { Expr::EllipsisLiteral(_) => true, Expr::Call(ExprCall { func, arguments, .. }) => { - if !checker.semantic().match_typing_expr(func, "cast") { + if !semantic.match_typing_expr(func, "cast") { return false; }