@@ -44,12 +44,13 @@ use crate::semantic_index::ast_ids::{HasScopedAstId, HasScopedUseId, ScopedExpre
44
44
use crate :: semantic_index:: definition:: { Definition , DefinitionKind , DefinitionNodeKey } ;
45
45
use crate :: semantic_index:: expression:: Expression ;
46
46
use crate :: semantic_index:: semantic_index;
47
- use crate :: semantic_index:: symbol:: { FileScopeId , NodeWithScopeKind , NodeWithScopeRef , ScopeId } ;
47
+ use crate :: semantic_index:: symbol:: { NodeWithScopeKind , NodeWithScopeRef , ScopeId } ;
48
48
use crate :: semantic_index:: SemanticIndex ;
49
49
use crate :: types:: diagnostic:: { TypeCheckDiagnostic , TypeCheckDiagnostics } ;
50
50
use crate :: types:: {
51
51
builtins_symbol_ty_by_name, definitions_ty, global_symbol_ty_by_name, symbol_ty,
52
- BytesLiteralType , ClassType , FunctionType , StringLiteralType , Type , UnionBuilder ,
52
+ symbol_ty_by_name, BytesLiteralType , ClassType , FunctionType , StringLiteralType , Type ,
53
+ UnionBuilder ,
53
54
} ;
54
55
use crate :: Db ;
55
56
@@ -301,7 +302,7 @@ impl<'db> TypeInferenceBuilder<'db> {
301
302
self . file . is_stub ( self . db . upcast ( ) )
302
303
}
303
304
304
- /// Are we currently inferred deferred types?
305
+ /// Are we currently inferring deferred types?
305
306
fn is_deferred ( & self ) -> bool {
306
307
matches ! ( self . region, InferenceRegion :: Deferred ( _) )
307
308
}
@@ -1823,6 +1824,61 @@ impl<'db> TypeInferenceBuilder<'db> {
1823
1824
Type :: Unknown
1824
1825
}
1825
1826
1827
+ /// Look up a name reference that isn't bound in the local scope.
1828
+ fn lookup_name ( & self , name : & ast:: name:: Name ) -> Type < ' db > {
1829
+ let file_scope_id = self . scope . file_scope_id ( self . db ) ;
1830
+ let symbols = self . index . symbol_table ( file_scope_id) ;
1831
+ let symbol = symbols
1832
+ . symbol_by_name ( name)
1833
+ . expect ( "Expected the symbol table to create a symbol for every Name node" ) ;
1834
+
1835
+ // In function-like scopes, any local variable (symbol that is defined in this
1836
+ // scope) can only have a definition in this scope, or be undefined; it never references
1837
+ // another scope. (At runtime, it would use the `LOAD_FAST` opcode.)
1838
+ if !symbol. is_defined ( ) || !self . scope . is_function_like ( self . db ) {
1839
+ // Walk up parent scopes looking for a possible enclosing scope that may have a
1840
+ // definition of this name visible to us (would be `LOAD_DEREF` at runtime.)
1841
+ for ( enclosing_scope_file_id, _) in self . index . ancestor_scopes ( file_scope_id) {
1842
+ // Class scopes are not visible to nested scopes, and we need to handle global
1843
+ // scope differently (because an unbound name there falls back to builtins), so
1844
+ // check only function-like scopes.
1845
+ let enclosing_scope_id = enclosing_scope_file_id. to_scope_id ( self . db , self . file ) ;
1846
+ if !enclosing_scope_id. is_function_like ( self . db ) {
1847
+ continue ;
1848
+ }
1849
+ let enclosing_symbol_table = self . index . symbol_table ( enclosing_scope_file_id) ;
1850
+ let Some ( enclosing_symbol) = enclosing_symbol_table. symbol_by_name ( name) else {
1851
+ continue ;
1852
+ } ;
1853
+ // TODO a "definition" that is just an annotated-assignment with no RHS should not
1854
+ // count as "is_defined" here.
1855
+ if enclosing_symbol. is_defined ( ) {
1856
+ // We can return early here, because the nearest function-like scope that
1857
+ // defines a name must be the only source for the nonlocal reference (at
1858
+ // runtime, it is the scope that creates the cell for our closure.) If the name
1859
+ // isn't bound in that scope, we should get an unbound name, not continue
1860
+ // falling back to other scopes / globals / builtins.
1861
+ return symbol_ty_by_name ( self . db , enclosing_scope_id, name) ;
1862
+ }
1863
+ }
1864
+ // No nonlocal binding, check module globals. Avoid infinite recursion if `self.scope`
1865
+ // already is module globals.
1866
+ let ty = if file_scope_id. is_global ( ) {
1867
+ Type :: Unbound
1868
+ } else {
1869
+ global_symbol_ty_by_name ( self . db , self . file , name)
1870
+ } ;
1871
+ // Fallback to builtins (without infinite recursion if we're already in builtins.)
1872
+ if ty. may_be_unbound ( self . db ) && Some ( self . scope ) != builtins_scope ( self . db ) {
1873
+ ty. replace_unbound_with ( self . db , builtins_symbol_ty_by_name ( self . db , name) )
1874
+ } else {
1875
+ ty
1876
+ }
1877
+ } else {
1878
+ Type :: Unbound
1879
+ }
1880
+ }
1881
+
1826
1882
fn infer_name_expression ( & mut self , name : & ast:: ExprName ) -> Type < ' db > {
1827
1883
let ast:: ExprName { range : _, id, ctx } = name;
1828
1884
let file_scope_id = self . scope . file_scope_id ( self . db ) ;
@@ -1843,30 +1899,7 @@ impl<'db> TypeInferenceBuilder<'db> {
1843
1899
let may_be_unbound = use_def. use_may_be_unbound ( use_id) ;
1844
1900
1845
1901
let unbound_ty = if may_be_unbound {
1846
- let symbols = self . index . symbol_table ( file_scope_id) ;
1847
- // SAFETY: the symbol table always creates a symbol for every Name node.
1848
- let symbol = symbols. symbol_by_name ( id) . unwrap ( ) ;
1849
- if !symbol. is_defined ( ) || !self . scope . is_function_like ( self . db ) {
1850
- // implicit global
1851
- let unbound_ty = if file_scope_id == FileScopeId :: global ( ) {
1852
- Type :: Unbound
1853
- } else {
1854
- global_symbol_ty_by_name ( self . db , self . file , id)
1855
- } ;
1856
- // fallback to builtins
1857
- if unbound_ty. may_be_unbound ( self . db )
1858
- && Some ( self . scope ) != builtins_scope ( self . db )
1859
- {
1860
- Some ( unbound_ty. replace_unbound_with (
1861
- self . db ,
1862
- builtins_symbol_ty_by_name ( self . db , id) ,
1863
- ) )
1864
- } else {
1865
- Some ( unbound_ty)
1866
- }
1867
- } else {
1868
- Some ( Type :: Unbound )
1869
- }
1902
+ Some ( self . lookup_name ( id) )
1870
1903
} else {
1871
1904
None
1872
1905
} ;
@@ -2385,6 +2418,31 @@ mod tests {
2385
2418
assert_eq ! ( ty. display( db) . to_string( ) , expected) ;
2386
2419
}
2387
2420
2421
+ fn assert_scope_ty (
2422
+ db : & TestDb ,
2423
+ file_name : & str ,
2424
+ scopes : & [ & str ] ,
2425
+ symbol_name : & str ,
2426
+ expected : & str ,
2427
+ ) {
2428
+ let file = system_path_to_file ( db, file_name) . expect ( "Expected file to exist." ) ;
2429
+ let index = semantic_index ( db, file) ;
2430
+ let mut file_scope_id = FileScopeId :: global ( ) ;
2431
+ let mut scope = file_scope_id. to_scope_id ( db, file) ;
2432
+ for expected_scope_name in scopes {
2433
+ file_scope_id = index
2434
+ . child_scopes ( file_scope_id)
2435
+ . next ( )
2436
+ . unwrap_or_else ( || panic ! ( "scope of {expected_scope_name}" ) )
2437
+ . 0 ;
2438
+ scope = file_scope_id. to_scope_id ( db, file) ;
2439
+ assert_eq ! ( scope. name( db) , * expected_scope_name) ;
2440
+ }
2441
+
2442
+ let ty = symbol_ty_by_name ( db, scope, symbol_name) ;
2443
+ assert_eq ! ( ty. display( db) . to_string( ) , expected) ;
2444
+ }
2445
+
2388
2446
#[ test]
2389
2447
fn follow_import_to_class ( ) -> anyhow:: Result < ( ) > {
2390
2448
let mut db = setup_db ( ) ;
@@ -3601,15 +3659,6 @@ mod tests {
3601
3659
Ok ( ( ) )
3602
3660
}
3603
3661
3604
- fn first_public_def < ' db > ( db : & ' db TestDb , file : File , name : & str ) -> Definition < ' db > {
3605
- let scope = global_scope ( db, file) ;
3606
- use_def_map ( db, scope)
3607
- . public_definitions ( symbol_table ( db, scope) . symbol_id_by_name ( name) . unwrap ( ) )
3608
- . next ( )
3609
- . unwrap ( )
3610
- . definition
3611
- }
3612
-
3613
3662
#[ test]
3614
3663
fn big_int ( ) -> anyhow:: Result < ( ) > {
3615
3664
let mut db = setup_db ( ) ;
@@ -3694,6 +3743,77 @@ mod tests {
3694
3743
Ok ( ( ) )
3695
3744
}
3696
3745
3746
+ #[ test]
3747
+ fn nonlocal_name_reference ( ) -> anyhow:: Result < ( ) > {
3748
+ let mut db = setup_db ( ) ;
3749
+
3750
+ db. write_dedented (
3751
+ "/src/a.py" ,
3752
+ "
3753
+ def f():
3754
+ x = 1
3755
+ def g():
3756
+ y = x
3757
+ " ,
3758
+ ) ?;
3759
+
3760
+ assert_scope_ty ( & db, "/src/a.py" , & [ "f" , "g" ] , "y" , "Literal[1]" ) ;
3761
+
3762
+ Ok ( ( ) )
3763
+ }
3764
+
3765
+ #[ test]
3766
+ fn nonlocal_name_reference_multi_level ( ) -> anyhow:: Result < ( ) > {
3767
+ let mut db = setup_db ( ) ;
3768
+
3769
+ db. write_dedented (
3770
+ "/src/a.py" ,
3771
+ "
3772
+ def f():
3773
+ x = 1
3774
+ def g():
3775
+ def h():
3776
+ y = x
3777
+ " ,
3778
+ ) ?;
3779
+
3780
+ assert_scope_ty ( & db, "/src/a.py" , & [ "f" , "g" , "h" ] , "y" , "Literal[1]" ) ;
3781
+
3782
+ Ok ( ( ) )
3783
+ }
3784
+
3785
+ #[ test]
3786
+ fn nonlocal_name_reference_skips_class_scope ( ) -> anyhow:: Result < ( ) > {
3787
+ let mut db = setup_db ( ) ;
3788
+
3789
+ db. write_dedented (
3790
+ "/src/a.py" ,
3791
+ "
3792
+ def f():
3793
+ x = 1
3794
+ class C:
3795
+ x = 2
3796
+ def g():
3797
+ y = x
3798
+ " ,
3799
+ ) ?;
3800
+
3801
+ assert_scope_ty ( & db, "/src/a.py" , & [ "f" , "C" , "g" ] , "y" , "Literal[1]" ) ;
3802
+
3803
+ Ok ( ( ) )
3804
+ }
3805
+
3806
+ // Incremental inference tests
3807
+
3808
+ fn first_public_def < ' db > ( db : & ' db TestDb , file : File , name : & str ) -> Definition < ' db > {
3809
+ let scope = global_scope ( db, file) ;
3810
+ use_def_map ( db, scope)
3811
+ . public_definitions ( symbol_table ( db, scope) . symbol_id_by_name ( name) . unwrap ( ) )
3812
+ . next ( )
3813
+ . unwrap ( )
3814
+ . definition
3815
+ }
3816
+
3697
3817
#[ test]
3698
3818
fn dependency_public_symbol_type_change ( ) -> anyhow:: Result < ( ) > {
3699
3819
let mut db = setup_db ( ) ;
0 commit comments