Skip to content

Commit 0bc6440

Browse files
committed
improve type safety
1 parent 5caf5b4 commit 0bc6440

File tree

2 files changed

+29
-4
lines changed

2 files changed

+29
-4
lines changed

crates/red_knot_python_semantic/src/semantic_index/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ impl<'db> SemanticIndexBuilder<'db> {
519519
}
520520

521521
/// Records a visibility constraint by applying it to all live bindings and declarations.
522+
#[must_use = "A visibility constraint must always be negated after it is added"]
522523
fn record_visibility_constraint(
523524
&mut self,
524525
predicate: Predicate<'db>,

crates/red_knot_python_semantic/src/semantic_index/use_def.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -701,24 +701,27 @@ impl<'db> UseDefMapBuilder<'db> {
701701
.add_and_constraint(self.scope_start_visibility, constraint);
702702
}
703703

704+
#[must_use = "A `*`-import visibility constraint must always be negated after it is added"]
704705
pub(super) fn record_star_import_visibility_constraint(
705706
&mut self,
706707
star_import: StarImportPlaceholderPredicate<'db>,
707708
symbol: ScopedSymbolId,
708-
) -> ScopedVisibilityConstraintId {
709+
) -> StarImportVisibilityConstraintId {
709710
let predicate_id = self.add_predicate(star_import.into());
710711
let visibility_id = self.visibility_constraints.add_atom(predicate_id);
711712
self.symbol_states[symbol]
712713
.record_visibility_constraint(&mut self.visibility_constraints, visibility_id);
713-
visibility_id
714+
StarImportVisibilityConstraintId(visibility_id)
714715
}
715716

716717
pub(super) fn negate_star_import_visibility_constraint(
717718
&mut self,
718719
symbol_id: ScopedSymbolId,
719-
constraint: ScopedVisibilityConstraintId,
720+
constraint: StarImportVisibilityConstraintId,
720721
) {
721-
let negated_constraint = self.visibility_constraints.add_not_constraint(constraint);
722+
let negated_constraint = self
723+
.visibility_constraints
724+
.add_not_constraint(constraint.into_scoped_constraint_id());
722725
self.symbol_states[symbol_id]
723726
.record_visibility_constraint(&mut self.visibility_constraints, negated_constraint);
724727
self.scope_start_visibility = self
@@ -925,3 +928,24 @@ impl<'db> UseDefMapBuilder<'db> {
925928
}
926929
}
927930
}
931+
932+
/// Newtype wrapper over [`ScopedVisibilityConstraintId`] to improve type safety.
933+
///
934+
/// By returning this type from [`UseDefMapBuilder::record_star_import_visibility_constraint`]
935+
/// rather than [`ScopedVisibilityConstraintId`] directly, we ensure that
936+
/// [`UseDefMapBuilder::negate_star_import_visibility_constraint`] must be called after the
937+
/// visibility constraint has been added, and we ensure that
938+
/// [`super::SemanticIndexBuilder::record_negated_visibility_constraint`] *cannot* be called with
939+
/// the narrowing constraint (which would lead to incorrect behaviour).
940+
///
941+
/// This type is defined here rather than in the [`super::visibility_constraints`] module
942+
/// because it should only ever be constructed and deconstructed from methods in the
943+
/// [`UseDefMapBuilder`].
944+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
945+
pub(super) struct StarImportVisibilityConstraintId(ScopedVisibilityConstraintId);
946+
947+
impl StarImportVisibilityConstraintId {
948+
fn into_scoped_constraint_id(self) -> ScopedVisibilityConstraintId {
949+
self.0
950+
}
951+
}

0 commit comments

Comments
 (0)