@@ -245,7 +245,7 @@ pub(crate) fn class_symbol<'db>(
245245 scope,
246246 symbol,
247247 RequiresExplicitReExport :: No ,
248- ConsideredDefinitions :: AllLiveAtUse ,
248+ ConsideredDefinitions :: EndOfScope ,
249249 ) ;
250250
251251 if symbol_and_quals. is_class_var ( ) {
@@ -262,12 +262,7 @@ pub(crate) fn class_symbol<'db>(
262262 // Otherwise, we need to check if the symbol has bindings
263263 let use_def = use_def_map ( db, scope) ;
264264 let bindings = use_def. end_of_scope_bindings ( symbol) ;
265- let inferred = place_from_bindings_impl (
266- db,
267- bindings,
268- RequiresExplicitReExport :: No ,
269- ConsideredDefinitions :: AllLiveAtUse ,
270- ) ;
265+ let inferred = place_from_bindings_impl ( db, bindings, RequiresExplicitReExport :: No ) ;
271266
272267 // TODO: we should not need to calculate inferred type second time. This is a temporary
273268 // solution until the notion of Boundness and Declaredness is split. See #16036, #16264
@@ -361,7 +356,7 @@ pub(crate) fn imported_symbol<'db>(
361356 global_scope ( db, file) ,
362357 name,
363358 requires_explicit_reexport,
364- ConsideredDefinitions :: AllLiveAtUse ,
359+ ConsideredDefinitions :: EndOfScope ,
365360 )
366361 . or_fall_back_to ( db, || {
367362 if name == "__getattr__" {
@@ -391,7 +386,7 @@ pub(crate) fn builtins_symbol<'db>(db: &'db dyn Db, symbol: &str) -> PlaceAndQua
391386 global_scope ( db, file) ,
392387 symbol,
393388 RequiresExplicitReExport :: Yes ,
394- ConsideredDefinitions :: AllLiveAtUse ,
389+ ConsideredDefinitions :: EndOfScope ,
395390 )
396391 . or_fall_back_to ( db, || {
397392 // We're looking up in the builtins namespace and not the module, so we should
@@ -462,14 +457,8 @@ fn core_module_scope(db: &dyn Db, core_module: KnownModule) -> Option<ScopeId<'_
462457pub ( super ) fn place_from_bindings < ' db > (
463458 db : & ' db dyn Db ,
464459 bindings_with_constraints : BindingWithConstraintsIterator < ' _ , ' db > ,
465- considered_definitions : ConsideredDefinitions , // TODO: remove this and always use `LiveBindingsAtUse` here?
466460) -> Place < ' db > {
467- place_from_bindings_impl (
468- db,
469- bindings_with_constraints,
470- RequiresExplicitReExport :: No ,
471- considered_definitions,
472- )
461+ place_from_bindings_impl ( db, bindings_with_constraints, RequiresExplicitReExport :: No )
473462}
474463
475464/// Build a declared type from a [`DeclarationsIterator`].
@@ -483,14 +472,8 @@ pub(super) fn place_from_bindings<'db>(
483472pub ( crate ) fn place_from_declarations < ' db > (
484473 db : & ' db dyn Db ,
485474 declarations : DeclarationsIterator < ' _ , ' db > ,
486- considered_definitions : ConsideredDefinitions ,
487475) -> PlaceFromDeclarationsResult < ' db > {
488- place_from_declarations_impl (
489- db,
490- declarations,
491- RequiresExplicitReExport :: No ,
492- considered_definitions,
493- )
476+ place_from_declarations_impl ( db, declarations, RequiresExplicitReExport :: No )
494477}
495478
496479/// The result of looking up a declared type from declarations; see [`place_from_declarations`].
@@ -653,20 +636,15 @@ fn place_by_id<'db>(
653636 // on inference from bindings.
654637
655638 let declarations = match considered_definitions {
656- ConsideredDefinitions :: AllLiveAtUse => use_def. end_of_scope_declarations ( place_id) ,
657- ConsideredDefinitions :: AllReachable => use_def. reachable_declarations ( place_id) ,
639+ ConsideredDefinitions :: EndOfScope => use_def. end_of_scope_declarations ( place_id) ,
640+ ConsideredDefinitions :: AllReachable => use_def. all_reachable_declarations ( place_id) ,
658641 } ;
659642
660- let declared = place_from_declarations_impl (
661- db,
662- declarations,
663- requires_explicit_reexport,
664- considered_definitions,
665- ) ;
643+ let declared = place_from_declarations_impl ( db, declarations, requires_explicit_reexport) ;
666644
667645 let all_considered_bindings = || match considered_definitions {
668- ConsideredDefinitions :: AllLiveAtUse => use_def. end_of_scope_bindings ( place_id) ,
669- ConsideredDefinitions :: AllReachable => use_def. reachable_bindings ( place_id) ,
646+ ConsideredDefinitions :: EndOfScope => use_def. end_of_scope_bindings ( place_id) ,
647+ ConsideredDefinitions :: AllReachable => use_def. all_reachable_bindings ( place_id) ,
670648 } ;
671649
672650 match declared {
@@ -683,12 +661,7 @@ fn place_by_id<'db>(
683661 qualifiers,
684662 } ) => {
685663 let bindings = all_considered_bindings ( ) ;
686- let inferred = place_from_bindings_impl (
687- db,
688- bindings,
689- requires_explicit_reexport,
690- considered_definitions,
691- ) ;
664+ let inferred = place_from_bindings_impl ( db, bindings, requires_explicit_reexport) ;
692665
693666 let place = match inferred {
694667 // Place is possibly undeclared and definitely unbound
@@ -713,12 +686,7 @@ fn place_by_id<'db>(
713686 qualifiers : _,
714687 } ) => {
715688 let bindings = all_considered_bindings ( ) ;
716- let inferred = place_from_bindings_impl (
717- db,
718- bindings,
719- requires_explicit_reexport,
720- considered_definitions,
721- ) ;
689+ let inferred = place_from_bindings_impl ( db, bindings, requires_explicit_reexport) ;
722690
723691 // `__slots__` is a symbol with special behavior in Python's runtime. It can be
724692 // modified externally, but those changes do not take effect. We therefore issue
@@ -842,10 +810,10 @@ fn place_from_bindings_impl<'db>(
842810 db : & ' db dyn Db ,
843811 bindings_with_constraints : BindingWithConstraintsIterator < ' _ , ' db > ,
844812 requires_explicit_reexport : RequiresExplicitReExport ,
845- considered_definitions : ConsideredDefinitions ,
846813) -> Place < ' db > {
847814 let predicates = bindings_with_constraints. predicates ;
848815 let reachability_constraints = bindings_with_constraints. reachability_constraints ;
816+ let considered_definitions = bindings_with_constraints. boundness_analysis ;
849817 let mut bindings_with_constraints = bindings_with_constraints. peekable ( ) ;
850818
851819 let is_non_exported = |binding : Definition < ' db > | {
@@ -865,7 +833,7 @@ fn place_from_bindings_impl<'db>(
865833 // Evaluate this lazily because we don't always need it (for example, if there are no visible
866834 // bindings at all, we don't need it), and it can cause us to evaluate reachability constraint
867835 // expressions, which is extra work and can lead to cycles.
868- let unbound_reachability = || {
836+ let unbound_visibility = || {
869837 unbound_reachability_constraint. map ( |reachability_constraint| {
870838 reachability_constraints. evaluate ( db, predicates, reachability_constraint)
871839 } )
@@ -945,7 +913,7 @@ fn place_from_bindings_impl<'db>(
945913 // return `Never` in this case, because we will union the types of all bindings, and
946914 // `Never` will be eliminated automatically.
947915
948- if unbound_reachability ( ) . is_none_or ( Truthiness :: is_always_false) {
916+ if unbound_visibility ( ) . is_none_or ( Truthiness :: is_always_false) {
949917 return Some ( Type :: Never ) ;
950918 }
951919 return None ;
@@ -958,23 +926,16 @@ fn place_from_bindings_impl<'db>(
958926
959927 if let Some ( first) = types. next ( ) {
960928 let boundness = match considered_definitions {
961- ConsideredDefinitions :: AllReachable => {
962- // TODO: explain why we do this
963- Boundness :: Bound
964- }
965- ConsideredDefinitions :: AllLiveAtUse => {
966- // If we have at least one binding, the implicit `unbound` binding should not be
967- // definitely visible.
968- match unbound_reachability ( ) {
969- Some ( Truthiness :: AlwaysTrue ) => {
970- unreachable ! (
971- "If we have at least one binding, the implicit `unbound` binding should not be definitely visible"
972- )
973- }
974- Some ( Truthiness :: AlwaysFalse ) | None => Boundness :: Bound ,
975- Some ( Truthiness :: Ambiguous ) => Boundness :: PossiblyUnbound ,
929+ BoundnessAnalysis :: AlwaysBound => Boundness :: Bound ,
930+ BoundnessAnalysis :: BasedOnUnboundVisibility => match unbound_visibility ( ) {
931+ Some ( Truthiness :: AlwaysTrue ) => {
932+ unreachable ! (
933+ "If we have at least one binding, the implicit `unbound` binding should not be definitely visible"
934+ )
976935 }
977- }
936+ Some ( Truthiness :: AlwaysFalse ) | None => Boundness :: Bound ,
937+ Some ( Truthiness :: Ambiguous ) => Boundness :: PossiblyUnbound ,
938+ } ,
978939 } ;
979940
980941 let ty = if let Some ( second) = types. next ( ) {
@@ -1079,10 +1040,10 @@ fn place_from_declarations_impl<'db>(
10791040 db : & ' db dyn Db ,
10801041 declarations : DeclarationsIterator < ' _ , ' db > ,
10811042 requires_explicit_reexport : RequiresExplicitReExport ,
1082- considered_definitions : ConsideredDefinitions ,
10831043) -> PlaceFromDeclarationsResult < ' db > {
10841044 let predicates = declarations. predicates ;
10851045 let reachability_constraints = declarations. reachability_constraints ;
1046+ let boundness_analysis = declarations. boundness_analysis ;
10861047 let mut declarations = declarations. peekable ( ) ;
10871048
10881049 let is_non_exported = |declaration : Definition < ' db > | {
@@ -1136,9 +1097,9 @@ fn place_from_declarations_impl<'db>(
11361097 return Err ( ( declared, conflicting) ) ;
11371098 }
11381099
1139- let boundness = match considered_definitions {
1140- ConsideredDefinitions :: AllReachable => Boundness :: Bound ,
1141- ConsideredDefinitions :: AllLiveAtUse => match undeclared_reachability {
1100+ let boundness = match boundness_analysis {
1101+ BoundnessAnalysis :: AlwaysBound => Boundness :: Bound ,
1102+ BoundnessAnalysis :: BasedOnUnboundVisibility => match undeclared_reachability {
11421103 Truthiness :: AlwaysTrue => {
11431104 unreachable ! (
11441105 "If we have at least one declaration, the implicit `unbound` binding should not be definitely visible"
@@ -1180,7 +1141,7 @@ mod implicit_globals {
11801141 use ruff_python_ast as ast;
11811142
11821143 use crate :: db:: Db ;
1183- use crate :: place:: { ConsideredDefinitions , PlaceAndQualifiers } ;
1144+ use crate :: place:: PlaceAndQualifiers ;
11841145 use crate :: semantic_index:: place:: PlaceExpr ;
11851146 use crate :: semantic_index:: { self , place_table, use_def_map} ;
11861147 use crate :: types:: { KnownClass , Type } ;
@@ -1209,7 +1170,6 @@ mod implicit_globals {
12091170 place_from_declarations (
12101171 db,
12111172 use_def_map ( db, module_type_scope) . end_of_scope_declarations ( place_id) ,
1212- ConsideredDefinitions :: AllLiveAtUse ,
12131173 )
12141174 }
12151175
@@ -1329,10 +1289,46 @@ impl RequiresExplicitReExport {
13291289 }
13301290}
13311291
1292+ /// Specifies which definitions should be considered when looking up a place.
1293+ ///
1294+ /// In the example below, the `EndOfScope` variant would consider the `x = 2` and `x = 3` definitions,
1295+ /// while the `AllReachable` variant would also consider the `x = 1` definition.
1296+ /// ```py
1297+ /// def _():
1298+ /// x = 1
1299+ ///
1300+ /// x = 2
1301+ ///
1302+ /// if flag():
1303+ /// x = 3
1304+ /// ```
13321305#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash , salsa:: Update ) ]
13331306pub ( crate ) enum ConsideredDefinitions {
1307+ /// Consider only the definitions that are "live" at the end of the scope, i.e. those
1308+ /// that have not been shadowed or deleted.
1309+ EndOfScope ,
1310+ /// Consider all definitions that are reachable from the start of the scope.
13341311 AllReachable ,
1335- AllLiveAtUse ,
1312+ }
1313+
1314+ /// Specifies how the boundness of a place should be determined.
1315+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash , salsa:: Update ) ]
1316+ pub ( crate ) enum BoundnessAnalysis {
1317+ /// The place is always considered bound.
1318+ AlwaysBound ,
1319+ /// The boundness of the place is determined based on the visibility of the implicit
1320+ /// `unbound` binding. In the example below, when analyzing the visibility of the
1321+ /// `x = <unbound>` binding from the position of the end of the scope, it would be
1322+ /// `Truthiness::Ambiguous`, because it could either be visible or not, depending on the
1323+ /// `flag()` return value. This would result in a `Boundness::PossiblyUnbound` for `x`.
1324+ ///
1325+ /// ```py
1326+ /// x = <unbound>
1327+ ///
1328+ /// if flag():
1329+ /// x = 1
1330+ /// ```
1331+ BasedOnUnboundVisibility ,
13361332}
13371333
13381334/// Computes a possibly-widened type `Unknown | T_inferred` from the inferred type `T_inferred`
0 commit comments