mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
[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:
parent
b0f01ba514
commit
64bcc8db2f
3 changed files with 36 additions and 2 deletions
|
@ -346,7 +346,10 @@ l: list[str | Literal[1] | None] = [None]
|
||||||
|
|
||||||
def f(x: str | Literal[1] | None):
|
def f(x: str | Literal[1] | None):
|
||||||
class C:
|
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 _():
|
def _():
|
||||||
if x != 1:
|
if x != 1:
|
||||||
reveal_type(x) # revealed: str | None
|
reveal_type(x) # revealed: str | None
|
||||||
|
|
|
@ -262,3 +262,28 @@ def f():
|
||||||
global __file__ # allowed, implicit global
|
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"
|
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
|
||||||
|
```
|
||||||
|
|
|
@ -6556,6 +6556,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
if let Some(use_id) = use_id {
|
if let Some(use_id) = use_id {
|
||||||
constraint_keys.push((file_scope_id, ConstraintKey::UseId(use_id)));
|
constraint_keys.push((file_scope_id, ConstraintKey::UseId(use_id)));
|
||||||
}
|
}
|
||||||
|
let local_is_unbound = local_scope_place.is_unbound();
|
||||||
|
|
||||||
let place = PlaceAndQualifiers::from(local_scope_place).or_fall_back_to(db, || {
|
let place = PlaceAndQualifiers::from(local_scope_place).or_fall_back_to(db, || {
|
||||||
let has_bindings_in_this_scope = match place_table.place_id(place_expr) {
|
let has_bindings_in_this_scope = match place_table.place_id(place_expr) {
|
||||||
|
@ -6574,7 +6575,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let mut is_nonlocal_binding = false;
|
let mut is_nonlocal_binding = false;
|
||||||
if let Some(symbol) = place_expr.as_symbol() {
|
if let Some(symbol) = place_expr.as_symbol() {
|
||||||
if let Some(symbol_id) = place_table.symbol_id(symbol.name()) {
|
if let Some(symbol_id) = place_table.symbol_id(symbol.name()) {
|
||||||
if self.skip_non_global_scopes(file_scope_id, symbol_id) {
|
// If we try to access a variable in a class before it has been defined,
|
||||||
|
// the lookup will fall back to global.
|
||||||
|
let fallback_to_global = local_is_unbound
|
||||||
|
&& has_bindings_in_this_scope
|
||||||
|
&& scope.node(db).scope_kind().is_class();
|
||||||
|
if self.skip_non_global_scopes(file_scope_id, symbol_id) || fallback_to_global {
|
||||||
return global_symbol(self.db(), self.file(), symbol.name()).map_type(
|
return global_symbol(self.db(), self.file(), symbol.name()).map_type(
|
||||||
|ty| {
|
|ty| {
|
||||||
self.narrow_place_with_applicable_constraints(
|
self.narrow_place_with_applicable_constraints(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue