11use ruff_db:: files:: File ;
2+ use salsa:: Update ;
23
34use crate :: dunder_all:: dunder_all_names;
45use crate :: module_resolver:: file_to_module;
@@ -202,8 +203,15 @@ pub(crate) fn symbol<'db>(
202203 db : & ' db dyn Db ,
203204 scope : ScopeId < ' db > ,
204205 name : & str ,
206+ considered_bindings : ConsideredBindings ,
205207) -> PlaceAndQualifiers < ' db > {
206- symbol_impl ( db, scope, name, RequiresExplicitReExport :: No )
208+ symbol_impl (
209+ db,
210+ scope,
211+ name,
212+ RequiresExplicitReExport :: No ,
213+ considered_bindings,
214+ )
207215}
208216
209217/// Infer the public type of a place (its type as seen from outside its scope) in the given
@@ -212,8 +220,15 @@ pub(crate) fn place<'db>(
212220 db : & ' db dyn Db ,
213221 scope : ScopeId < ' db > ,
214222 expr : & PlaceExpr ,
223+ considered_bindings : ConsideredBindings ,
215224) -> PlaceAndQualifiers < ' db > {
216- place_impl ( db, scope, expr, RequiresExplicitReExport :: No )
225+ place_impl (
226+ db,
227+ scope,
228+ expr,
229+ RequiresExplicitReExport :: No ,
230+ considered_bindings,
231+ )
217232}
218233
219234/// Infer the public type of a class symbol (its type as seen from outside its scope) in the given
@@ -226,7 +241,13 @@ pub(crate) fn class_symbol<'db>(
226241 place_table ( db, scope)
227242 . place_id_by_name ( name)
228243 . map ( |symbol| {
229- let symbol_and_quals = place_by_id ( db, scope, symbol, RequiresExplicitReExport :: No ) ;
244+ let symbol_and_quals = place_by_id (
245+ db,
246+ scope,
247+ symbol,
248+ RequiresExplicitReExport :: No ,
249+ ConsideredBindings :: LiveBindingsAtUse ,
250+ ) ;
230251
231252 if symbol_and_quals. is_class_var ( ) {
232253 // For declared class vars we do not need to check if they have bindings,
@@ -241,8 +262,13 @@ pub(crate) fn class_symbol<'db>(
241262 {
242263 // Otherwise, we need to check if the symbol has bindings
243264 let use_def = use_def_map ( db, scope) ;
244- let bindings = use_def. public_bindings ( symbol) ;
245- let inferred = place_from_bindings_impl ( db, bindings, RequiresExplicitReExport :: No ) ;
265+ let bindings = use_def. end_of_scope_bindings ( symbol) ;
266+ let inferred = place_from_bindings_impl (
267+ db,
268+ bindings,
269+ RequiresExplicitReExport :: No ,
270+ ConsideredBindings :: LiveBindingsAtUse ,
271+ ) ;
246272
247273 // TODO: we should not need to calculate inferred type second time. This is a temporary
248274 // solution until the notion of Boundness and Declaredness is split. See #16036, #16264
@@ -277,6 +303,7 @@ pub(crate) fn explicit_global_symbol<'db>(
277303 global_scope ( db, file) ,
278304 name,
279305 RequiresExplicitReExport :: No ,
306+ ConsideredBindings :: AllReachable ,
280307 )
281308}
282309
@@ -330,18 +357,22 @@ pub(crate) fn imported_symbol<'db>(
330357 // ignore `__getattr__`. Typeshed has a fake `__getattr__` on `types.ModuleType` to help out with
331358 // dynamic imports; we shouldn't use it for `ModuleLiteral` types where we know exactly which
332359 // module we're dealing with.
333- symbol_impl ( db , global_scope ( db , file ) , name , requires_explicit_reexport ) . or_fall_back_to (
360+ symbol_impl (
334361 db,
335- || {
336- if name == "__getattr__" {
337- Place :: Unbound . into ( )
338- } else if name == "__builtins__" {
339- Place :: bound ( Type :: any ( ) ) . into ( )
340- } else {
341- KnownClass :: ModuleType . to_instance ( db) . member ( db, name)
342- }
343- } ,
362+ global_scope ( db, file) ,
363+ name,
364+ requires_explicit_reexport,
365+ ConsideredBindings :: LiveBindingsAtUse ,
344366 )
367+ . or_fall_back_to ( db, || {
368+ if name == "__getattr__" {
369+ Place :: Unbound . into ( )
370+ } else if name == "__builtins__" {
371+ Place :: bound ( Type :: any ( ) ) . into ( )
372+ } else {
373+ KnownClass :: ModuleType . to_instance ( db) . member ( db, name)
374+ }
375+ } )
345376}
346377
347378/// Lookup the type of `symbol` in the builtins namespace.
@@ -361,6 +392,7 @@ pub(crate) fn builtins_symbol<'db>(db: &'db dyn Db, symbol: &str) -> PlaceAndQua
361392 global_scope ( db, file) ,
362393 symbol,
363394 RequiresExplicitReExport :: Yes ,
395+ ConsideredBindings :: LiveBindingsAtUse ,
364396 )
365397 . or_fall_back_to ( db, || {
366398 // We're looking up in the builtins namespace and not the module, so we should
@@ -431,8 +463,14 @@ fn core_module_scope(db: &dyn Db, core_module: KnownModule) -> Option<ScopeId<'_
431463pub ( super ) fn place_from_bindings < ' db > (
432464 db : & ' db dyn Db ,
433465 bindings_with_constraints : BindingWithConstraintsIterator < ' _ , ' db > ,
466+ considered_bindings : ConsideredBindings , // TODO: remove this and always use `LiveBindingsAtUse` here?
434467) -> Place < ' db > {
435- place_from_bindings_impl ( db, bindings_with_constraints, RequiresExplicitReExport :: No )
468+ place_from_bindings_impl (
469+ db,
470+ bindings_with_constraints,
471+ RequiresExplicitReExport :: No ,
472+ considered_bindings,
473+ )
436474}
437475
438476/// Build a declared type from a [`DeclarationsIterator`].
@@ -581,6 +619,7 @@ fn place_cycle_recover<'db>(
581619 _scope : ScopeId < ' db > ,
582620 _place_id : ScopedPlaceId ,
583621 _requires_explicit_reexport : RequiresExplicitReExport ,
622+ _considered_bindings : ConsideredBindings ,
584623) -> salsa:: CycleRecoveryAction < PlaceAndQualifiers < ' db > > {
585624 salsa:: CycleRecoveryAction :: Iterate
586625}
@@ -590,6 +629,7 @@ fn place_cycle_initial<'db>(
590629 _scope : ScopeId < ' db > ,
591630 _place_id : ScopedPlaceId ,
592631 _requires_explicit_reexport : RequiresExplicitReExport ,
632+ _considered_bindings : ConsideredBindings ,
593633) -> PlaceAndQualifiers < ' db > {
594634 Place :: bound ( Type :: Never ) . into ( )
595635}
@@ -600,6 +640,7 @@ fn place_by_id<'db>(
600640 scope : ScopeId < ' db > ,
601641 place_id : ScopedPlaceId ,
602642 requires_explicit_reexport : RequiresExplicitReExport ,
643+ considered_bindings : ConsideredBindings ,
603644) -> PlaceAndQualifiers < ' db > {
604645 let use_def = use_def_map ( db, scope) ;
605646
@@ -609,6 +650,11 @@ fn place_by_id<'db>(
609650 let declarations = use_def. public_declarations ( place_id) ;
610651 let declared = place_from_declarations_impl ( db, declarations, requires_explicit_reexport) ;
611652
653+ let all_considered_bindings = || match considered_bindings {
654+ ConsideredBindings :: LiveBindingsAtUse => use_def. end_of_scope_bindings ( place_id) ,
655+ ConsideredBindings :: AllReachable => use_def. potentially_reachable_bindings ( place_id) ,
656+ } ;
657+
612658 match declared {
613659 // Place is declared, trust the declared type
614660 Ok (
@@ -622,8 +668,13 @@ fn place_by_id<'db>(
622668 place : Place :: Type ( declared_ty, Boundness :: PossiblyUnbound ) ,
623669 qualifiers,
624670 } ) => {
625- let bindings = use_def. all_bindings ( place_id) ;
626- let inferred = place_from_bindings_impl ( db, bindings, requires_explicit_reexport) ;
671+ let bindings = all_considered_bindings ( ) ;
672+ let inferred = place_from_bindings_impl (
673+ db,
674+ bindings,
675+ requires_explicit_reexport,
676+ considered_bindings,
677+ ) ;
627678
628679 let place = match inferred {
629680 // Place is possibly undeclared and definitely unbound
@@ -647,8 +698,13 @@ fn place_by_id<'db>(
647698 place : Place :: Unbound ,
648699 qualifiers : _,
649700 } ) => {
650- let bindings = use_def. all_bindings ( place_id) ;
651- let inferred = place_from_bindings_impl ( db, bindings, requires_explicit_reexport) ;
701+ let bindings = all_considered_bindings ( ) ;
702+ let inferred = place_from_bindings_impl (
703+ db,
704+ bindings,
705+ requires_explicit_reexport,
706+ considered_bindings,
707+ ) ;
652708
653709 // `__slots__` is a symbol with special behavior in Python's runtime. It can be
654710 // modified externally, but those changes do not take effect. We therefore issue
@@ -707,6 +763,7 @@ fn symbol_impl<'db>(
707763 scope : ScopeId < ' db > ,
708764 name : & str ,
709765 requires_explicit_reexport : RequiresExplicitReExport ,
766+ considered_bindings : ConsideredBindings ,
710767) -> PlaceAndQualifiers < ' db > {
711768 let _span = tracing:: trace_span!( "symbol" , ?name) . entered ( ) ;
712769
@@ -726,7 +783,15 @@ fn symbol_impl<'db>(
726783
727784 place_table ( db, scope)
728785 . place_id_by_name ( name)
729- . map ( |symbol| place_by_id ( db, scope, symbol, requires_explicit_reexport) )
786+ . map ( |symbol| {
787+ place_by_id (
788+ db,
789+ scope,
790+ symbol,
791+ requires_explicit_reexport,
792+ considered_bindings,
793+ )
794+ } )
730795 . unwrap_or_default ( )
731796}
732797
@@ -736,12 +801,21 @@ fn place_impl<'db>(
736801 scope : ScopeId < ' db > ,
737802 expr : & PlaceExpr ,
738803 requires_explicit_reexport : RequiresExplicitReExport ,
804+ considered_bindings : ConsideredBindings ,
739805) -> PlaceAndQualifiers < ' db > {
740806 let _span = tracing:: trace_span!( "place" , ?expr) . entered ( ) ;
741807
742808 place_table ( db, scope)
743809 . place_id_by_expr ( expr)
744- . map ( |place| place_by_id ( db, scope, place, requires_explicit_reexport) )
810+ . map ( |place| {
811+ place_by_id (
812+ db,
813+ scope,
814+ place,
815+ requires_explicit_reexport,
816+ considered_bindings,
817+ )
818+ } )
745819 . unwrap_or_default ( )
746820}
747821
@@ -754,6 +828,7 @@ fn place_from_bindings_impl<'db>(
754828 db : & ' db dyn Db ,
755829 bindings_with_constraints : BindingWithConstraintsIterator < ' _ , ' db > ,
756830 requires_explicit_reexport : RequiresExplicitReExport ,
831+ considered_bindings : ConsideredBindings ,
757832) -> Place < ' db > {
758833 let predicates = bindings_with_constraints. predicates ;
759834 let reachability_constraints = bindings_with_constraints. reachability_constraints ;
@@ -868,15 +943,24 @@ fn place_from_bindings_impl<'db>(
868943 ) ;
869944
870945 if let Some ( first) = types. next ( ) {
871- let boundness = match unbound_reachability ( ) {
872- Some ( Truthiness :: AlwaysTrue ) => {
873- // unreachable!(
874- // "If we have at least one binding, the implicit `unbound` binding should not be definitely visible"
875- // )
876- Boundness :: Bound // TODO
946+ let boundness = match considered_bindings {
947+ ConsideredBindings :: AllReachable => {
948+ // TODO: explain why we do this
949+ Boundness :: Bound
950+ }
951+ ConsideredBindings :: LiveBindingsAtUse => {
952+ // If we have at least one binding, the implicit `unbound` binding should not be
953+ // definitely visible.
954+ match unbound_reachability ( ) {
955+ Some ( Truthiness :: AlwaysTrue ) => {
956+ unreachable ! (
957+ "If we have at least one binding, the implicit `unbound` binding should not be definitely visible"
958+ )
959+ }
960+ Some ( Truthiness :: AlwaysFalse ) | None => Boundness :: Bound ,
961+ Some ( Truthiness :: Ambiguous ) => Boundness :: PossiblyUnbound ,
962+ }
877963 }
878- Some ( Truthiness :: AlwaysFalse ) | None => Boundness :: Bound ,
879- Some ( Truthiness :: Ambiguous ) => Boundness :: PossiblyUnbound ,
880964 } ;
881965
882966 let ty = if let Some ( second) = types. next ( ) {
@@ -1166,6 +1250,12 @@ impl RequiresExplicitReExport {
11661250 }
11671251}
11681252
1253+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash , Update ) ]
1254+ pub ( crate ) enum ConsideredBindings {
1255+ AllReachable ,
1256+ LiveBindingsAtUse ,
1257+ }
1258+
11691259/// Computes a possibly-widened type `Unknown | T_inferred` from the inferred type `T_inferred`
11701260/// of a symbol, unless the type is a known-instance type (e.g. `typing.Any`) or the symbol is
11711261/// considered non-modifiable (e.g. when the symbol is `@Final`). We need this for public uses
0 commit comments