@@ -29,15 +29,16 @@ use ruff_db::parsed::parsed_module;
29
29
use ruff_python_ast as ast;
30
30
use ruff_python_ast:: { ExprContext , TypeParams } ;
31
31
32
+ use crate :: builtins:: builtins_scope;
32
33
use crate :: semantic_index:: ast_ids:: { HasScopedAstId , HasScopedUseId , ScopedExpressionId } ;
33
34
use crate :: semantic_index:: definition:: { Definition , DefinitionKind , DefinitionNodeKey } ;
34
35
use crate :: semantic_index:: expression:: Expression ;
35
36
use crate :: semantic_index:: semantic_index;
36
- use crate :: semantic_index:: symbol:: NodeWithScopeKind ;
37
- use crate :: semantic_index:: symbol:: { NodeWithScopeRef , ScopeId } ;
37
+ use crate :: semantic_index:: symbol:: { FileScopeId , NodeWithScopeKind , NodeWithScopeRef , ScopeId } ;
38
38
use crate :: semantic_index:: SemanticIndex ;
39
39
use crate :: types:: {
40
- definitions_ty, global_symbol_ty_by_name, ClassType , FunctionType , Name , Type , UnionTypeBuilder ,
40
+ builtins_symbol_ty_by_name, definitions_ty, global_symbol_ty_by_name, ClassType , FunctionType ,
41
+ Name , Type , UnionTypeBuilder ,
41
42
} ;
42
43
use crate :: Db ;
43
44
@@ -686,7 +687,18 @@ impl<'db> TypeInferenceBuilder<'db> {
686
687
let symbol = symbols. symbol_by_name ( id) . unwrap ( ) ;
687
688
if !symbol. is_defined ( ) || !self . scope . is_function_like ( self . db ) {
688
689
// implicit global
689
- Some ( global_symbol_ty_by_name ( self . db , self . file , id) )
690
+ let mut unbound_ty = if file_scope_id == FileScopeId :: global ( ) {
691
+ Type :: Unbound
692
+ } else {
693
+ global_symbol_ty_by_name ( self . db , self . file , id)
694
+ } ;
695
+ // fallback to builtins
696
+ if matches ! ( unbound_ty, Type :: Unbound )
697
+ && Some ( self . scope ) != builtins_scope ( self . db )
698
+ {
699
+ unbound_ty = builtins_symbol_ty_by_name ( self . db , id) ;
700
+ }
701
+ Some ( unbound_ty)
690
702
} else {
691
703
Some ( Type :: Unbound )
692
704
}
@@ -792,6 +804,7 @@ mod tests {
792
804
use ruff_db:: testing:: assert_function_query_was_not_run;
793
805
use ruff_python_ast:: name:: Name ;
794
806
807
+ use crate :: builtins:: builtins_scope;
795
808
use crate :: db:: tests:: TestDb ;
796
809
use crate :: semantic_index:: definition:: Definition ;
797
810
use crate :: semantic_index:: semantic_index;
@@ -819,6 +832,23 @@ mod tests {
819
832
db
820
833
}
821
834
835
+ fn setup_db_with_custom_typeshed ( typeshed : & str ) -> TestDb {
836
+ let db = TestDb :: new ( ) ;
837
+
838
+ Program :: new (
839
+ & db,
840
+ TargetVersion :: Py38 ,
841
+ SearchPathSettings {
842
+ extra_paths : Vec :: new ( ) ,
843
+ workspace_root : SystemPathBuf :: from ( "/src" ) ,
844
+ site_packages : None ,
845
+ custom_typeshed : Some ( SystemPathBuf :: from ( typeshed) ) ,
846
+ } ,
847
+ ) ;
848
+
849
+ db
850
+ }
851
+
822
852
fn assert_public_ty ( db : & TestDb , file_name : & str , symbol_name : & str , expected : & str ) {
823
853
let file = system_path_to_file ( db, file_name) . expect ( "Expected file to exist." ) ;
824
854
@@ -1370,6 +1400,80 @@ mod tests {
1370
1400
Ok ( ( ) )
1371
1401
}
1372
1402
1403
+ #[ test]
1404
+ fn builtin_symbol_vendored_stdlib ( ) -> anyhow:: Result < ( ) > {
1405
+ let mut db = setup_db ( ) ;
1406
+
1407
+ db. write_file ( "/src/a.py" , "c = copyright" ) ?;
1408
+
1409
+ assert_public_ty ( & db, "/src/a.py" , "c" , "Literal[copyright]" ) ;
1410
+
1411
+ Ok ( ( ) )
1412
+ }
1413
+
1414
+ #[ test]
1415
+ fn builtin_symbol_custom_stdlib ( ) -> anyhow:: Result < ( ) > {
1416
+ let mut db = setup_db_with_custom_typeshed ( "/typeshed" ) ;
1417
+
1418
+ db. write_files ( [
1419
+ ( "/src/a.py" , "c = copyright" ) ,
1420
+ (
1421
+ "/typeshed/stdlib/builtins.pyi" ,
1422
+ "def copyright() -> None: ..." ,
1423
+ ) ,
1424
+ ( "/typeshed/stdlib/VERSIONS" , "builtins: 3.8-" ) ,
1425
+ ] ) ?;
1426
+
1427
+ assert_public_ty ( & db, "/src/a.py" , "c" , "Literal[copyright]" ) ;
1428
+
1429
+ Ok ( ( ) )
1430
+ }
1431
+
1432
+ #[ test]
1433
+ fn unknown_global_later_defined ( ) -> anyhow:: Result < ( ) > {
1434
+ let mut db = setup_db ( ) ;
1435
+
1436
+ db. write_file ( "/src/a.py" , "x = foo; foo = 1" ) ?;
1437
+
1438
+ assert_public_ty ( & db, "/src/a.py" , "x" , "Unbound" ) ;
1439
+
1440
+ Ok ( ( ) )
1441
+ }
1442
+
1443
+ #[ test]
1444
+ fn unknown_builtin_later_defined ( ) -> anyhow:: Result < ( ) > {
1445
+ let mut db = setup_db_with_custom_typeshed ( "/typeshed" ) ;
1446
+
1447
+ db. write_files ( [
1448
+ ( "/src/a.py" , "x = foo" ) ,
1449
+ ( "/typeshed/stdlib/builtins.pyi" , "foo = bar; bar = 1" ) ,
1450
+ ( "/typeshed/stdlib/VERSIONS" , "builtins: 3.8-" ) ,
1451
+ ] ) ?;
1452
+
1453
+ assert_public_ty ( & db, "/src/a.py" , "x" , "Unbound" ) ;
1454
+
1455
+ Ok ( ( ) )
1456
+ }
1457
+
1458
+ #[ test]
1459
+ fn import_builtins ( ) -> anyhow:: Result < ( ) > {
1460
+ let mut db = setup_db ( ) ;
1461
+
1462
+ db. write_file ( "/src/a.py" , "import builtins; x = builtins.copyright" ) ?;
1463
+
1464
+ assert_public_ty ( & db, "/src/a.py" , "x" , "Literal[copyright]" ) ;
1465
+ // imported builtins module is the same file as the implicit builtins
1466
+ let file = system_path_to_file ( & db, "/src/a.py" ) . expect ( "Expected file to exist." ) ;
1467
+ let builtins_ty = global_symbol_ty_by_name ( & db, file, "builtins" ) ;
1468
+ let Type :: Module ( builtins_file) = builtins_ty else {
1469
+ panic ! ( "Builtins are not a module?" ) ;
1470
+ } ;
1471
+ let implicit_builtins_file = builtins_scope ( & db) . expect ( "builtins to exist" ) . file ( & db) ;
1472
+ assert_eq ! ( builtins_file, implicit_builtins_file) ;
1473
+
1474
+ Ok ( ( ) )
1475
+ }
1476
+
1373
1477
fn first_public_def < ' db > ( db : & ' db TestDb , file : File , name : & str ) -> Definition < ' db > {
1374
1478
let scope = global_scope ( db, file) ;
1375
1479
* use_def_map ( db, scope)
0 commit comments