From 46decd4feb0e286ad59c6ebf7eb7e2a67f85b876 Mon Sep 17 00:00:00 2001 From: Dan Parizher <105245560+danparizher@users.noreply.github.com> Date: Tue, 23 Sep 2025 10:56:39 -0400 Subject: [PATCH] [`pyupgrade`] Fix `UP008` to not apply when `__class__` is a local variable (`UP008`) (#20497) ## Summary Fixes #20491 --- .../resources/test/fixtures/pyupgrade/UP008.py | 16 ++++++++++++++++ .../rules/super_call_with_parameters.rs | 10 +++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py index dd46c6c4d0..96c3acc75d 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py +++ b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py @@ -287,3 +287,19 @@ class C(B): def f(self): C = B # Local variable C shadows the class name return super(C, self).f() # Should NOT trigger UP008 + + +# See: https://github.com/astral-sh/ruff/issues/20491 +# UP008 should not apply when __class__ is a local variable +class A: + def f(self): + return 1 + +class B(A): + def f(self): + return 2 + +class C(B): + def f(self): + __class__ = B # Local variable __class__ shadows the implicit __class__ + return super(__class__, self).f() # Should NOT trigger UP008 diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs index 30ff93d6be..9f7f31a2f8 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs @@ -139,11 +139,11 @@ pub(crate) fn super_call_with_parameters(checker: &Checker, call: &ast::ExprCall return; }; - if !((first_arg_id == "__class__" - || (first_arg_id == parent_name.as_str() - // If the first argument matches the class name, check if it's a local variable - // that shadows the class name. If so, don't apply UP008. - && !checker.semantic().current_scope().has(first_arg_id))) + // The `super(__class__, self)` and `super(ParentClass, self)` patterns are redundant in Python 3 + // when the first argument refers to the implicit `__class__` cell or to the enclosing class. + // Avoid triggering if a local variable shadows either name. + if !(((first_arg_id == "__class__") || (first_arg_id == parent_name.as_str())) + && !checker.semantic().current_scope().has(first_arg_id) && second_arg_id == parent_arg.name().as_str()) { return;