ruff
dc3637d0 - [ty] Track unused-binding captures across nested scopes (#25536)

Commit
25 days ago
[ty] Track unused-binding captures across nested scopes (#25536) ## Summary We previously determined whether an enclosing binding was used by reusing the snapshots created for type inference. This misses captures that originate in a descendant scope across a lazy function boundary: ```python def outer(i: int): def inner(): return [[k for k in range(i)] for _ in range(2)] return inner ``` The read of `i` belongs to a comprehension scope nested under `inner`. Snapshot traversal stops at the lazy `inner` scope, so the final unused-binding pass could not connect that descendant read to `outer`'s parameter and incorrectly reported `i` as unused. This tracks capture usage independently from type-resolution snapshots. When a scope completes, unresolved free-name uses are propagated to its parent until the completed scope that owns the name can claim them. We defer that resolution until each candidate enclosing scope is complete because a later binding can change which scope owns the name: ```python def outer(): x = 0 def middle(): def inner(): return x x = 1 return inner return middle ``` Here, Python treats `x` as local to `middle` even though its binding follows `inner`, so `inner` captures `x = 1` and `outer`'s `x = 0` remains unused. Pending captures retain the bindings visible when the nested scope was created and, for lazy scopes, later bindings and reassignments that can reach the capture. Regression coverage includes nested comprehensions, captured parameters, shadowed outer bindings, bindings introduced after nested functions, intermediate reassignments, and `nonlocal` proxy scopes. Closes https://github.com/astral-sh/ty/issues/3442. --------- Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
Author
Parents
Loading