Skip to content

Commit 751b05e

Browse files
committed
Doc comments
1 parent 4bd957a commit 751b05e

File tree

2 files changed

+68
-12
lines changed

2 files changed

+68
-12
lines changed

crates/red_knot_python_semantic/src/semantic_index/builder.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,19 @@ impl<'db> SemanticIndexBuilder<'db> {
546546
.simplify_visibility_constraints(snapshot);
547547
}
548548

549+
/// Record a constraint that affects the reachability of the current position in the semantic
550+
/// index analysis. For example, if we encounter a `if test:` branch, we immediately record
551+
/// a `test` constraint, because if `test` later (during type checking) evaluates to `False`,
552+
/// we know that all statements that follow in this path of control flow will be unreachable.
553+
fn record_reachability_constraint(
554+
&mut self,
555+
predicate: Predicate<'db>,
556+
) -> ScopedVisibilityConstraintId {
557+
let predicate_id = self.add_predicate(predicate);
558+
self.record_reachability_constraint_id(predicate_id)
559+
}
560+
561+
/// Similar to [`record_reachability_constraint`], but takes a [`ScopedPredicateId`].
549562
fn record_reachability_constraint_id(
550563
&mut self,
551564
predicate_id: ScopedPredicateId,
@@ -557,14 +570,7 @@ impl<'db> SemanticIndexBuilder<'db> {
557570
.record_reachability_constraint(visibility_constraint)
558571
}
559572

560-
fn record_reachability_constraint(
561-
&mut self,
562-
predicate: Predicate<'db>,
563-
) -> ScopedVisibilityConstraintId {
564-
let predicate_id = self.add_predicate(predicate);
565-
self.record_reachability_constraint_id(predicate_id)
566-
}
567-
573+
/// Record the negation of a given reachability/visibility constraint.
568574
fn record_negated_reachability_constraint(
569575
&mut self,
570576
reachability_constraint: ScopedVisibilityConstraintId,

crates/red_knot_python_semantic/src/semantic_index/use_def.rs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ pub(crate) struct UseDefMap<'db> {
297297
/// [`SymbolBindings`] reaching a [`ScopedUseId`].
298298
bindings_by_use: IndexVec<ScopedUseId, SymbolBindings>,
299299

300+
/// Tracks whether or not a given use of a symbol is reachable from the start of the scope.
300301
reachability_by_use: IndexVec<ScopedUseId, ScopedVisibilityConstraintId>,
301302

302303
/// If the definition is a binding (only) -- `x = 1` for example -- then we need
@@ -347,6 +348,17 @@ impl<'db> UseDefMap<'db> {
347348
self.bindings_iterator(&self.bindings_by_use[use_id])
348349
}
349350

351+
/// Returns true if a given 'use' of a symbol is reachable from the start of the scope.
352+
/// For example, in the following code, use `2` is reachable, but `1` and `3` are not:
353+
/// ```py
354+
/// def f():
355+
/// x = 1
356+
/// if False:
357+
/// x # 1
358+
/// x # 2
359+
/// return
360+
/// x # 3
361+
/// ```
350362
pub(crate) fn is_symbol_use_reachable(&self, db: &dyn crate::Db, use_id: ScopedUseId) -> bool {
351363
!self
352364
.visibility_constraints
@@ -560,17 +572,55 @@ pub(super) struct UseDefMapBuilder<'db> {
560572
pub(super) visibility_constraints: VisibilityConstraintsBuilder,
561573

562574
/// A constraint which describes the visibility of the unbound/undeclared state, i.e.
563-
/// whether or not the start of the scope is visible. This is important for cases like
564-
/// `if True: x = 1; use(x)` where we need to hide the implicit "x = unbound" binding
565-
/// in the "else" branch.
575+
/// whether or not a use of a symbol at the current point in control flow would see
576+
/// the fake "x = <unbound>" binding at the start of the scope. This is important for
577+
/// cases like the following, where we need to hide the implicit unbound binding in
578+
/// the "else" branch:
579+
/// ```py
580+
/// # x = <unbound>
581+
///
582+
/// if True:
583+
/// x = 1
584+
///
585+
/// use(x) # the `x = <unbound>` binding is not visible here
586+
/// ```
566587
pub(super) scope_start_visibility: ScopedVisibilityConstraintId,
567588

568589
/// Live bindings at each so-far-recorded use.
569590
bindings_by_use: IndexVec<ScopedUseId, SymbolBindings>,
570591

571-
/// Whether or not the scope start is visible for a given use of a symbol.
592+
/// Tracks whether or not the scope start is visible at the current point in control flow.
593+
/// This is subtly different from `scope_start_visibility`, as we apply these constraints
594+
/// at the beginnging of a branch. Visibility constraints, on the other hand, need to be
595+
/// applied at the end of a branch, as we apply them retroactively to all live bindings:
596+
/// ```py
597+
/// y = 1
598+
///
599+
/// if test:
600+
/// # we record a reachability constraint of [test] here,
601+
/// # so that it can affect the use of `x`:
602+
///
603+
/// x # we store a reachability constraint of [test] for this use of `x`
604+
///
605+
/// y = 2
606+
///
607+
/// # we record a visibility constraint of [test] here, which retroactively affects
608+
/// # the `y = 1` and the `y = 2` binding.
609+
/// else:
610+
/// # we record a reachability constraint of [~test] here.
611+
///
612+
/// pass
613+
///
614+
/// # we record a visibility constraint of [~test] here, which retroactively affects
615+
/// # the `y = 1` binding.
616+
///
617+
/// use(y)
618+
/// ```
619+
/// Depending on the value of `test`, the `y = 1`, `y = 2`, or both bindings may be visible.
620+
/// The use of `x` is recorded with a reachability constraint of `[test]`.
572621
reachability: ScopedVisibilityConstraintId,
573622

623+
/// Tracks whether or not a given use of a symbol is reachable from the start of the scope.
574624
reachability_by_use: IndexVec<ScopedUseId, ScopedVisibilityConstraintId>,
575625

576626
/// Live declarations for each so-far-recorded binding.

0 commit comments

Comments
 (0)