Skip to content

Commit 66fe226

Browse files
authored
[red-knot] fix lookup of nonlocal names in deferred annotations (#13236)
Initially I had deferred annotation name lookups reuse the "public symbol type", since that gives the correct "from end of scope" view of reaching definitions that we want. But there is a key difference; public symbol types are based only on definitions in the queried scope (or "name in the given namespace" in runtime terms), they don't ever look up a name in nonlocal/global/builtin scopes. Deferred annotation resolution should do this lookup. Add a test, and fix deferred name resolution to support nonlocal/global/builtin names. Fixes #13176
1 parent e965f9c commit 66fe226

File tree

1 file changed

+45
-18
lines changed
  • crates/red_knot_python_semantic/src/types

1 file changed

+45
-18
lines changed

crates/red_knot_python_semantic/src/types/infer.rs

+45-18
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ use crate::semantic_index::SemanticIndex;
4848
use crate::stdlib::builtins_module_scope;
4949
use crate::types::diagnostic::{TypeCheckDiagnostic, TypeCheckDiagnostics};
5050
use crate::types::{
51-
builtins_symbol_ty, definitions_ty, global_symbol_ty, symbol_ty, symbol_ty_by_id,
52-
BytesLiteralType, ClassType, FunctionType, StringLiteralType, Type, UnionBuilder,
51+
builtins_symbol_ty, definitions_ty, global_symbol_ty, symbol_ty, BytesLiteralType, ClassType,
52+
FunctionType, StringLiteralType, Type, UnionBuilder,
5353
};
5454
use crate::Db;
5555

@@ -1865,15 +1865,17 @@ impl<'db> TypeInferenceBuilder<'db> {
18651865
/// Look up a name reference that isn't bound in the local scope.
18661866
fn lookup_name(&self, name: &ast::name::Name) -> Type<'db> {
18671867
let file_scope_id = self.scope.file_scope_id(self.db);
1868-
let symbols = self.index.symbol_table(file_scope_id);
1869-
let symbol = symbols
1868+
let is_defined = self
1869+
.index
1870+
.symbol_table(file_scope_id)
18701871
.symbol_by_name(name)
1871-
.expect("Expected the symbol table to create a symbol for every Name node");
1872+
.expect("Symbol table should create a symbol for every Name node")
1873+
.is_defined();
18721874

18731875
// In function-like scopes, any local variable (symbol that is defined in this
18741876
// scope) can only have a definition in this scope, or be undefined; it never references
18751877
// another scope. (At runtime, it would use the `LOAD_FAST` opcode.)
1876-
if !symbol.is_defined() || !self.scope.is_function_like(self.db) {
1878+
if !is_defined || !self.scope.is_function_like(self.db) {
18771879
// Walk up parent scopes looking for a possible enclosing scope that may have a
18781880
// definition of this name visible to us (would be `LOAD_DEREF` at runtime.)
18791881
for (enclosing_scope_file_id, _) in self.index.ancestor_scopes(file_scope_id) {
@@ -1921,28 +1923,35 @@ impl<'db> TypeInferenceBuilder<'db> {
19211923
let ast::ExprName { range: _, id, ctx } = name;
19221924
let file_scope_id = self.scope.file_scope_id(self.db);
19231925

1924-
// if we're inferring types of deferred expressions, always treat them as public symbols
1925-
if self.is_deferred() {
1926-
let symbols = self.index.symbol_table(file_scope_id);
1927-
let symbol = symbols
1928-
.symbol_id_by_name(id)
1929-
.expect("Expected the symbol table to create a symbol for every Name node");
1930-
return symbol_ty_by_id(self.db, self.scope, symbol);
1931-
}
1932-
19331926
match ctx {
19341927
ExprContext::Load => {
19351928
let use_def = self.index.use_def_map(file_scope_id);
1936-
let use_id = name.scoped_use_id(self.db, self.scope);
1937-
let may_be_unbound = use_def.use_may_be_unbound(use_id);
1929+
let symbol = self
1930+
.index
1931+
.symbol_table(file_scope_id)
1932+
.symbol_id_by_name(id)
1933+
.expect("Expected the symbol table to create a symbol for every Name node");
1934+
// if we're inferring types of deferred expressions, always treat them as public symbols
1935+
let (definitions, may_be_unbound) = if self.is_deferred() {
1936+
(
1937+
use_def.public_definitions(symbol),
1938+
use_def.public_may_be_unbound(symbol),
1939+
)
1940+
} else {
1941+
let use_id = name.scoped_use_id(self.db, self.scope);
1942+
(
1943+
use_def.use_definitions(use_id),
1944+
use_def.use_may_be_unbound(use_id),
1945+
)
1946+
};
19381947

19391948
let unbound_ty = if may_be_unbound {
19401949
Some(self.lookup_name(id))
19411950
} else {
19421951
None
19431952
};
19441953

1945-
definitions_ty(self.db, use_def.use_definitions(use_id), unbound_ty)
1954+
definitions_ty(self.db, definitions, unbound_ty)
19461955
}
19471956
ExprContext::Store | ExprContext::Del => Type::None,
19481957
ExprContext::Invalid => Type::Unknown,
@@ -3686,6 +3695,24 @@ mod tests {
36863695
Ok(())
36873696
}
36883697

3698+
#[test]
3699+
fn deferred_annotation_builtin() -> anyhow::Result<()> {
3700+
let mut db = setup_db();
3701+
db.write_file("/src/a.pyi", "class C(object): pass")?;
3702+
let file = system_path_to_file(&db, "/src/a.pyi").unwrap();
3703+
let ty = global_symbol_ty(&db, file, "C");
3704+
3705+
let base = ty
3706+
.expect_class()
3707+
.bases(&db)
3708+
.next()
3709+
.expect("there should be at least one base");
3710+
3711+
assert_eq!(base.display(&db).to_string(), "Literal[object]");
3712+
3713+
Ok(())
3714+
}
3715+
36893716
#[test]
36903717
fn narrow_not_none() -> anyhow::Result<()> {
36913718
let mut db = setup_db();

0 commit comments

Comments
 (0)