From 91995aa5169175de55494bc3bb59781c7a3097fc Mon Sep 17 00:00:00 2001 From: Dan Parizher <105245560+danparizher@users.noreply.github.com> Date: Thu, 18 Sep 2025 10:05:05 -0400 Subject: [PATCH] [`pyupgrade`] Fix false positive when class name is shadowed by local variable (`UP008`) (#20427) ## Summary Fixes #20422 --------- Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com> --- .../resources/test/fixtures/pyupgrade/UP008.py | 16 ++++++++++++++++ .../rules/super_call_with_parameters.rs | 6 +++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py index be5b629cab..dd46c6c4d0 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py +++ b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py @@ -271,3 +271,19 @@ class ChildI9(ParentI): if False: super if False: __class__ builtins.super(ChildI9, self).f() + + +# See: https://github.com/astral-sh/ruff/issues/20422 +# UP008 should not apply when the class variable is shadowed +class A: + def f(self): + return 1 + +class B(A): + def f(self): + return 2 + +class C(B): + def f(self): + C = B # Local variable C shadows the class name + return super(C, 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 c6dd7f2811..30ff93d6be 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,7 +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 !((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))) && second_arg_id == parent_arg.name().as_str()) { return;