[ty] fix lookup order of class variables before they are defined (#19743)

## Summary

This is a follow-up to #19321.

If we try to access a class variable before it is defined, the variable
is looked up in the global scope, rather than in any enclosing scopes.

Closes https://github.com/astral-sh/ty/issues/875.

## Test Plan

New tests in `narrow/conditionals/nested.md`.
This commit is contained in:
Shunsuke Shibayama 2025-08-05 12:21:28 +09:00 committed by GitHub
parent b0f01ba514
commit 64bcc8db2f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 36 additions and 2 deletions

View file

@ -346,7 +346,10 @@ l: list[str | Literal[1] | None] = [None]
def f(x: str | Literal[1] | None):
class C:
if x is not None: # TODO: should be an unresolved-reference error
# If we try to access a variable in a class before it has been defined,
# the lookup will fall back to global.
# error: [unresolved-reference]
if x is not None:
def _():
if x != 1:
reveal_type(x) # revealed: str | None

View file

@ -262,3 +262,28 @@ def f():
global __file__ # allowed, implicit global
global int # error: [unresolved-global] "Invalid global declaration of `int`: `int` has no declarations or bindings in the global scope"
```
## References to variables before they are defined within a class scope are considered global
If we try to access a variable in a class before it has been defined, the lookup will fall back to
global.
```py
x: str = "a"
def f(x: int, y: int):
class C:
reveal_type(x) # revealed: int
class D:
x = None
reveal_type(x) # revealed: None
class E:
reveal_type(x) # revealed: str
x = None
# error: [unresolved-reference]
reveal_type(y) # revealed: Unknown
y = None
```