[red-knot] Resolve symbols from builtins.pyi in the stdlib if they … · astral-sh/ruff@d8cf8ac (original) (raw)

`@@ -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;

`

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)

`