[perflint] Fix PERF403 panic on attribute or subscription loop variable (#19042)

## Summary

Fixes: https://github.com/astral-sh/ruff/issues/19005

## Test Plan

Reproducer from issue report plus some extra cases that would cause the
panic were added.
This commit is contained in:
Robsdedude 2025-06-30 14:47:49 +00:00 committed by GitHub
parent 4fbf7e9de8
commit eb9d9c3646
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 31 additions and 2 deletions

View file

@ -170,3 +170,25 @@ def foo():
v = {}
for o,(x,)in():
v[x,]=o
# https://github.com/astral-sh/ruff/issues/19005
def issue_19005_1():
c = {}
a = object()
for a.b in ():
c[a.b] = a.b
def issue_19005_2():
a = object()
c = {}
for a.k, a.v in ():
c[a.k] = a.v
def issue_19005_3():
a = [None, None]
c = {}
for a[0], a[1] in ():
c[a[0]] = a[1]

View file

@ -462,12 +462,16 @@ fn has_post_loop_references(checker: &Checker, expr: &Expr, loop_end: TextSize)
.iter()
.any(|expr| has_post_loop_references(checker, expr, loop_end)),
Expr::Name(name) => {
let target_binding = checker
let Some(target_binding) = checker
.semantic()
.bindings
.iter()
.find(|binding| name.range() == binding.range)
.expect("for-loop target binding must exist");
else {
// no binding in for statement => err on the safe side and make the checker skip
// e.g., `for foo[0] in bar:` or `for foo.bar in baz:`
return true;
};
target_binding
.references()

View file

@ -374,3 +374,6 @@ PERF403.py:172:9: PERF403 [*] Use a dictionary comprehension instead of a for-lo
171 |- for o,(x,)in():
172 |- v[x,]=o
170 |+ v = {x: o for o,(x,) in ()}
173 171 |
174 172 |
175 173 | # https://github.com/astral-sh/ruff/issues/19005