[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

@ -6556,6 +6556,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
if let Some(use_id) = 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 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;
if let Some(symbol) = place_expr.as_symbol() {
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(
|ty| {
self.narrow_place_with_applicable_constraints(