@@ -32,7 +32,7 @@ use crate::semantic_index::{
3232 use_def_map, BindingWithConstraints , BindingWithConstraintsIterator , DeclarationWithConstraint ,
3333 DeclarationsIterator ,
3434} ;
35- use crate :: stdlib:: { builtins_symbol , known_module_symbol, typing_extensions_symbol} ;
35+ use crate :: stdlib:: { known_module_symbol, typing_extensions_symbol} ;
3636use crate :: suppression:: check_suppressions;
3737use crate :: symbol:: { Boundness , Symbol } ;
3838use crate :: types:: call:: {
@@ -107,40 +107,37 @@ fn widen_type_for_undeclared_public_symbol<'db>(
107107}
108108
109109#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
110- pub ( crate ) enum SymbolLookup {
111- /// Look up the symbol as seen from within the same module.
112- Internal ,
113- /// Look up the symbol as seen from outside the module.
114- External ,
110+ enum RequiresExplicitReExport {
111+ Yes ,
112+ No ,
115113}
116114
117- impl SymbolLookup {
118- const fn is_external ( self ) -> bool {
119- matches ! ( self , Self :: External )
115+ impl RequiresExplicitReExport {
116+ const fn is_yes ( self ) -> bool {
117+ matches ! ( self , RequiresExplicitReExport :: Yes )
120118 }
121119}
122120
123- /// Infer the public type of a symbol (its type as seen from outside its scope).
124- fn symbol < ' db > (
121+ fn symbol_impl < ' db > (
125122 db : & ' db dyn Db ,
126- lookup : SymbolLookup ,
127123 scope : ScopeId < ' db > ,
128124 name : & str ,
125+ requires_explicit_reexport : RequiresExplicitReExport ,
129126) -> Symbol < ' db > {
130127 #[ salsa:: tracked]
131128 fn symbol_by_id < ' db > (
132129 db : & ' db dyn Db ,
133- lookup : SymbolLookup ,
134130 scope : ScopeId < ' db > ,
135131 symbol_id : ScopedSymbolId ,
132+ requires_explicit_reexport : RequiresExplicitReExport ,
136133 ) -> Symbol < ' db > {
137134 let use_def = use_def_map ( db, scope) ;
138135
139136 // If the symbol is declared, the public type is based on declarations; otherwise, it's based
140137 // on inference from bindings.
141138
142139 let declarations = use_def. public_declarations ( symbol_id) ;
143- let declared = symbol_from_declarations ( db, lookup , declarations ) ;
140+ let declared = symbol_from_declarations ( db, declarations , requires_explicit_reexport ) ;
144141 let is_final = declared. as_ref ( ) . is_ok_and ( SymbolAndQualifiers :: is_final) ;
145142 let declared = declared. map ( |SymbolAndQualifiers ( symbol, _) | symbol) ;
146143
@@ -150,7 +147,7 @@ fn symbol<'db>(
150147 // Symbol is possibly declared
151148 Ok ( Symbol :: Type ( declared_ty, Boundness :: PossiblyUnbound ) ) => {
152149 let bindings = use_def. public_bindings ( symbol_id) ;
153- let inferred = symbol_from_bindings ( db, lookup , bindings ) ;
150+ let inferred = symbol_from_bindings ( db, bindings , requires_explicit_reexport ) ;
154151
155152 match inferred {
156153 // Symbol is possibly undeclared and definitely unbound
@@ -170,7 +167,7 @@ fn symbol<'db>(
170167 // Symbol is undeclared, return the union of `Unknown` with the inferred type
171168 Ok ( Symbol :: Unbound ) => {
172169 let bindings = use_def. public_bindings ( symbol_id) ;
173- let inferred = symbol_from_bindings ( db, lookup , bindings ) ;
170+ let inferred = symbol_from_bindings ( db, bindings , requires_explicit_reexport ) ;
174171
175172 // `__slots__` is a symbol with special behavior in Python's runtime. It can be
176173 // modified externally, but those changes do not take effect. We therefore issue
@@ -232,7 +229,7 @@ fn symbol<'db>(
232229
233230 symbol_table ( db, scope)
234231 . symbol_id_by_name ( name)
235- . map ( |symbol| symbol_by_id ( db, lookup , scope, symbol) )
232+ . map ( |symbol| symbol_by_id ( db, scope, symbol, requires_explicit_reexport ) )
236233 . unwrap_or ( Symbol :: Unbound )
237234}
238235
@@ -271,27 +268,99 @@ fn module_type_symbols<'db>(db: &'db dyn Db) -> smallvec::SmallVec<[ast::name::N
271268 . collect ( )
272269}
273270
274- pub ( crate ) fn global_symbol < ' db > (
275- db : & ' db dyn Db ,
276- lookup : SymbolLookup ,
277- file : File ,
278- name : & str ,
279- ) -> Symbol < ' db > {
280- // Not defined explicitly in the global scope?
281- // All modules are instances of `types.ModuleType`;
282- // look it up there (with a few very special exceptions)
283- symbol ( db, lookup, global_scope ( db, file) , name) . or_fall_back_to ( db, || {
284- if module_type_symbols ( db)
285- . iter ( )
286- . any ( |module_type_member| & * * module_type_member == name)
287- {
288- KnownClass :: ModuleType . to_instance ( db) . member ( db, name)
289- } else {
271+ /// Return the symbol for a member of `types.ModuleType`.
272+ pub ( crate ) fn module_type_symbol < ' db > ( db : & ' db dyn Db , name : & str ) -> Symbol < ' db > {
273+ if module_type_symbols ( db)
274+ . iter ( )
275+ . any ( |module_type_member| & * * module_type_member == name)
276+ {
277+ KnownClass :: ModuleType . to_instance ( db) . member ( db, name)
278+ } else {
279+ Symbol :: Unbound
280+ }
281+ }
282+
283+ /// Infer the public type of a symbol (its type as seen from outside its scope) in the given
284+ /// `scope`.
285+ fn symbol < ' db > ( db : & ' db dyn Db , scope : ScopeId < ' db > , name : & str ) -> Symbol < ' db > {
286+ symbol_impl ( db, scope, name, RequiresExplicitReExport :: No )
287+ }
288+
289+ /// Infers the public type of a module-global symbol as seen from within the same file.
290+ ///
291+ /// If it's not defined explicitly in the global scope, it will look it up in `types.ModuleType`
292+ /// with a few very special exceptions.
293+ ///
294+ /// Use [`imported_symbol`] to perform the lookup as seen from outside the file (e.g. via imports).
295+ pub ( crate ) fn global_symbol < ' db > ( db : & ' db dyn Db , file : File , name : & str ) -> Symbol < ' db > {
296+ symbol_impl (
297+ db,
298+ global_scope ( db, file) ,
299+ name,
300+ RequiresExplicitReExport :: No ,
301+ )
302+ . or_fall_back_to ( db, || module_type_symbol ( db, name) )
303+ }
304+
305+ /// Infers the public type of an imported symbol.
306+ pub ( crate ) fn imported_symbol < ' db > ( db : & ' db dyn Db , module : & Module , name : & str ) -> Symbol < ' db > {
307+ // If it's not found in the global scope, check if it's present as an instance on
308+ // `types.ModuleType` or `builtins.object`.
309+ //
310+ // We do a more limited version of this in `global_symbol`, but there are two crucial
311+ // differences here:
312+ // - If a member is looked up as an attribute, `__init__` is also available on the module, but
313+ // it isn't available as a global from inside the module
314+ // - If a member is looked up as an attribute, members on `builtins.object` are also available
315+ // (because `types.ModuleType` inherits from `object`); these attributes are also not
316+ // available as globals from inside the module.
317+ //
318+ // The same way as in `global_symbol`, however, we need to be careful to ignore
319+ // `__getattr__`. Typeshed has a fake `__getattr__` on `types.ModuleType` to help out with
320+ // dynamic imports; we shouldn't use it for `ModuleLiteral` types where we know exactly which
321+ // module we're dealing with.
322+ external_symbol_impl ( db, module. file ( ) , name) . or_fall_back_to ( db, || {
323+ if name == "__getattr__" {
290324 Symbol :: Unbound
325+ } else {
326+ KnownClass :: ModuleType . to_instance ( db) . member ( db, name)
291327 }
292328 } )
293329}
294330
331+ /// Lookup the type of `symbol` in the builtins namespace.
332+ ///
333+ /// Returns `Symbol::Unbound` if the `builtins` module isn't available for some reason.
334+ ///
335+ /// Note that this function is only intended for use in the context of the builtins *namespace*
336+ /// and should not be used when a symbol is being explicitly imported from the `builtins` module
337+ /// (e.g. `from builtins import int`).
338+ pub ( crate ) fn builtins_symbol < ' db > ( db : & ' db dyn Db , symbol : & str ) -> Symbol < ' db > {
339+ resolve_module ( db, & KnownModule :: Builtins . name ( ) )
340+ . map ( |module| {
341+ external_symbol_impl ( db, module. file ( ) , symbol) . or_fall_back_to ( db, || {
342+ // We're looking up in the builtins namespace and not the module, so we should
343+ // do the normal lookup in `types.ModuleType` and not the special one as in
344+ // `imported_symbol`.
345+ module_type_symbol ( db, symbol)
346+ } )
347+ } )
348+ . unwrap_or ( Symbol :: Unbound )
349+ }
350+
351+ fn external_symbol_impl < ' db > ( db : & ' db dyn Db , file : File , name : & str ) -> Symbol < ' db > {
352+ symbol_impl (
353+ db,
354+ global_scope ( db, file) ,
355+ name,
356+ if file. is_stub ( db. upcast ( ) ) {
357+ RequiresExplicitReExport :: Yes
358+ } else {
359+ RequiresExplicitReExport :: No
360+ } ,
361+ )
362+ }
363+
295364/// Infer the type of a binding.
296365pub ( crate ) fn binding_type < ' db > ( db : & ' db dyn Db , definition : Definition < ' db > ) -> Type < ' db > {
297366 let inference = infer_definition_types ( db, definition) ;
@@ -340,14 +409,14 @@ fn definition_expression_type<'db>(
340409/// The type will be a union if there are multiple bindings with different types.
341410fn symbol_from_bindings < ' db > (
342411 db : & ' db dyn Db ,
343- lookup : SymbolLookup ,
344412 bindings_with_constraints : BindingWithConstraintsIterator < ' _ , ' db > ,
413+ requires_explicit_reexport : RequiresExplicitReExport ,
345414) -> Symbol < ' db > {
346415 let visibility_constraints = bindings_with_constraints. visibility_constraints ;
347416 let mut bindings_with_constraints = bindings_with_constraints. peekable ( ) ;
348417
349418 let is_non_exported = |binding : Definition < ' db > | {
350- lookup . is_external ( ) && !binding. is_reexported ( db ) && binding . in_stub ( db)
419+ requires_explicit_reexport . is_yes ( ) && !binding. is_reexported ( db)
351420 } ;
352421
353422 let unbound_visibility = match bindings_with_constraints. peek ( ) {
@@ -471,14 +540,14 @@ type SymbolFromDeclarationsResult<'db> =
471540/// [`TypeQualifiers`] that have been specified on the declaration(s).
472541fn symbol_from_declarations < ' db > (
473542 db : & ' db dyn Db ,
474- lookup : SymbolLookup ,
475543 declarations : DeclarationsIterator < ' _ , ' db > ,
544+ requires_explicit_reexport : RequiresExplicitReExport ,
476545) -> SymbolFromDeclarationsResult < ' db > {
477546 let visibility_constraints = declarations. visibility_constraints ;
478547 let mut declarations = declarations. peekable ( ) ;
479548
480549 let is_non_exported = |declaration : Definition < ' db > | {
481- lookup . is_external ( ) && !declaration. is_reexported ( db ) && declaration . in_stub ( db)
550+ requires_explicit_reexport . is_yes ( ) && !declaration. is_reexported ( db)
482551 } ;
483552
484553 let undeclared_visibility = match declarations. peek ( ) {
@@ -3839,31 +3908,7 @@ impl<'db> ModuleLiteralType<'db> {
38393908 }
38403909 }
38413910
3842- // If it's not found in the global scope, check if it's present as an instance
3843- // on `types.ModuleType` or `builtins.object`.
3844- //
3845- // We do a more limited version of this in `global_symbol_ty`,
3846- // but there are two crucial differences here:
3847- // - If a member is looked up as an attribute, `__init__` is also available
3848- // on the module, but it isn't available as a global from inside the module
3849- // - If a member is looked up as an attribute, members on `builtins.object`
3850- // are also available (because `types.ModuleType` inherits from `object`);
3851- // these attributes are also not available as globals from inside the module.
3852- //
3853- // The same way as in `global_symbol_ty`, however, we need to be careful to
3854- // ignore `__getattr__`. Typeshed has a fake `__getattr__` on `types.ModuleType`
3855- // to help out with dynamic imports; we shouldn't use it for `ModuleLiteral` types
3856- // where we know exactly which module we're dealing with.
3857- global_symbol ( db, SymbolLookup :: External , self . module ( db) . file ( ) , name) . or_fall_back_to (
3858- db,
3859- || {
3860- if name == "__getattr__" {
3861- Symbol :: Unbound
3862- } else {
3863- KnownClass :: ModuleType . to_instance ( db) . member ( db, name)
3864- }
3865- } ,
3866- )
3911+ imported_symbol ( db, & self . module ( db) , name)
38673912 }
38683913}
38693914
@@ -4198,7 +4243,7 @@ impl<'db> Class<'db> {
41984243 /// traverse through the MRO until it finds the member.
41994244 pub ( crate ) fn own_class_member ( self , db : & ' db dyn Db , name : & str ) -> Symbol < ' db > {
42004245 let scope = self . body_scope ( db) ;
4201- symbol ( db, SymbolLookup :: Internal , scope, name)
4246+ symbol ( db, scope, name)
42024247 }
42034248
42044249 /// Returns the `name` attribute of an instance of this class.
@@ -4340,7 +4385,7 @@ impl<'db> Class<'db> {
43404385
43414386 let declarations = use_def. public_declarations ( symbol_id) ;
43424387
4343- match symbol_from_declarations ( db, SymbolLookup :: Internal , declarations ) {
4388+ match symbol_from_declarations ( db, declarations , RequiresExplicitReExport :: No ) {
43444389 Ok ( SymbolAndQualifiers ( Symbol :: Type ( declared_ty, _) , qualifiers) ) => {
43454390 // The attribute is declared in the class body.
43464391
@@ -4362,7 +4407,7 @@ impl<'db> Class<'db> {
43624407 // in a method, and it could also be *bound* in the class body (and/or in a method).
43634408
43644409 let bindings = use_def. public_bindings ( symbol_id) ;
4365- let inferred = symbol_from_bindings ( db, SymbolLookup :: Internal , bindings ) ;
4410+ let inferred = symbol_from_bindings ( db, bindings , RequiresExplicitReExport :: No ) ;
43664411 let inferred_ty = inferred. ignore_possibly_unbound ( ) ;
43674412
43684413 Self :: implicit_instance_attribute ( db, body_scope, name, inferred_ty) . into ( )
@@ -4980,7 +5025,7 @@ pub(crate) mod tests {
49805025 ) ?;
49815026
49825027 let bar = system_path_to_file ( & db, "src/bar.py" ) ?;
4983- let a = global_symbol ( & db, SymbolLookup :: Internal , bar, "a" ) ;
5028+ let a = global_symbol ( & db, bar, "a" ) ;
49845029
49855030 assert_eq ! (
49865031 a. expect_type( ) ,
@@ -4999,7 +5044,7 @@ pub(crate) mod tests {
49995044 ) ?;
50005045 db. clear_salsa_events ( ) ;
50015046
5002- let a = global_symbol ( & db, SymbolLookup :: Internal , bar, "a" ) ;
5047+ let a = global_symbol ( & db, bar, "a" ) ;
50035048
50045049 assert_eq ! (
50055050 a. expect_type( ) ,
0 commit comments