diff --git a/crates/red_knot_python_semantic/src/semantic_index/builder.rs b/crates/red_knot_python_semantic/src/semantic_index/builder.rs index 27e657aba9..c2b9c77e59 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/builder.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/builder.rs @@ -205,7 +205,11 @@ impl<'db> SemanticIndexBuilder<'db> { } fn flow_merge(&mut self, state: FlowSnapshot) { - self.current_use_def_map_mut().merge(state); + self.current_use_def_map_mut().merge(state, false); + } + + fn flow_merge_no_declarations(&mut self, state: FlowSnapshot) { + self.current_use_def_map_mut().merge(state, true); } fn add_symbol(&mut self, name: Name) -> ScopedSymbolId { @@ -1002,7 +1006,7 @@ where // Prepare for visiting the `except` block(s) self.flow_restore(pre_try_block_state); for state in try_block_snapshots { - self.flow_merge(state); + self.flow_merge_no_declarations(state); } let pre_except_state = self.flow_snapshot(); diff --git a/crates/red_knot_python_semantic/src/semantic_index/use_def.rs b/crates/red_knot_python_semantic/src/semantic_index/use_def.rs index 9f3e197c74..390d99be02 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/use_def.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/use_def.rs @@ -544,7 +544,7 @@ impl<'db> UseDefMapBuilder<'db> { /// Merge the given snapshot into the current state, reflecting that we might have taken either /// path to get here. The new state for each symbol should include definitions from both the /// prior state and the snapshot. - pub(super) fn merge(&mut self, snapshot: FlowSnapshot) { + pub(super) fn merge(&mut self, snapshot: FlowSnapshot, exclude_declarations: bool) { // We never remove symbols from `symbol_states` (it's an IndexVec, and the symbol // IDs must line up), so the current number of known symbols must always be equal to or // greater than the number of known symbols in a previously-taken snapshot. @@ -553,7 +553,7 @@ impl<'db> UseDefMapBuilder<'db> { let mut snapshot_definitions_iter = snapshot.symbol_states.into_iter(); for current in &mut self.symbol_states { if let Some(snapshot) = snapshot_definitions_iter.next() { - current.merge(snapshot); + current.merge(snapshot, exclude_declarations); } else { // Symbol not present in snapshot, so it's unbound/undeclared from that path. current.set_may_be_unbound(); diff --git a/crates/red_knot_python_semantic/src/semantic_index/use_def/symbol_state.rs b/crates/red_knot_python_semantic/src/semantic_index/use_def/symbol_state.rs index 506300067c..e735280fc5 100644 --- a/crates/red_knot_python_semantic/src/semantic_index/use_def/symbol_state.rs +++ b/crates/red_knot_python_semantic/src/semantic_index/use_def/symbol_state.rs @@ -227,24 +227,32 @@ impl SymbolState { } /// Merge another [`SymbolState`] into this one. - pub(super) fn merge(&mut self, b: SymbolState) { + pub(super) fn merge(&mut self, b: SymbolState, exclude_declarations: bool) { let mut a = Self { bindings: SymbolBindings { live_bindings: Bindings::default(), constraints: Constraints::default(), may_be_unbound: self.bindings.may_be_unbound || b.bindings.may_be_unbound, }, - declarations: SymbolDeclarations { - live_declarations: self.declarations.live_declarations.clone(), - may_be_undeclared: self.declarations.may_be_undeclared - || b.declarations.may_be_undeclared, + declarations: { + if exclude_declarations { + self.declarations.clone() + } else { + SymbolDeclarations { + live_declarations: self.declarations.live_declarations.clone(), + may_be_undeclared: self.declarations.may_be_undeclared + || b.declarations.may_be_undeclared, + } + } }, }; std::mem::swap(&mut a, self); - self.declarations - .live_declarations - .union(&b.declarations.live_declarations); + if !exclude_declarations { + self.declarations + .live_declarations + .union(&b.declarations.live_declarations); + } let mut a_defs_iter = a.bindings.live_bindings.iter(); let mut b_defs_iter = b.bindings.live_bindings.iter(); @@ -494,7 +502,7 @@ mod tests { sym0b.record_binding(ScopedDefinitionId::from_u32(0)); sym0b.record_constraint(ScopedConstraintId::from_u32(0)); - sym0a.merge(sym0b); + sym0a.merge(sym0b, false); let mut sym0 = sym0a; assert_bindings(&sym0, false, &["0<0>"]); @@ -507,7 +515,7 @@ mod tests { sym1b.record_binding(ScopedDefinitionId::from_u32(1)); sym1b.record_constraint(ScopedConstraintId::from_u32(2)); - sym1a.merge(sym1b); + sym1a.merge(sym1b, false); let sym1 = sym1a; assert_bindings(&sym1, false, &["1<>"]); @@ -518,12 +526,12 @@ mod tests { let sym2b = SymbolState::undefined(); - sym2a.merge(sym2b); + sym2a.merge(sym2b, false); let sym2 = sym2a; assert_bindings(&sym2, true, &["2<3>"]); // merging different definitions keeps them each with their existing constraints - sym0.merge(sym2); + sym0.merge(sym2, false); let sym = sym0; assert_bindings(&sym, true, &["0<0>", "2<3>"]); } @@ -560,7 +568,7 @@ mod tests { let mut sym2 = SymbolState::undefined(); sym2.record_declaration(ScopedDefinitionId::from_u32(2)); - sym.merge(sym2); + sym.merge(sym2, false); assert_declarations(&sym, false, &[1, 2]); } @@ -572,7 +580,7 @@ mod tests { let sym2 = SymbolState::undefined(); - sym.merge(sym2); + sym.merge(sym2, false); assert_declarations(&sym, true, &[1]); }