Skip to content

Commit 78d8b69

Browse files
committed
Refactoring
1 parent 11de7fb commit 78d8b69

File tree

7 files changed

+191
-225
lines changed

7 files changed

+191
-225
lines changed

crates/ty_python_semantic/src/place.rs

Lines changed: 67 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -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<'_
462457
pub(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>(
483472
pub(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)]
13331306
pub(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

Comments
 (0)