mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:43 +00:00
[red-knot] small efficiency improvements and bugfixes to use-def map building (#12373)
Adds inference tests sufficient to give full test coverage of the `UseDefMapBuilder::merge` method. In the process I realized that we could implement visiting of if statements in `SemanticBuilder` with fewer `snapshot`, `restore`, and `merge` operations, so I restructured that visit a bit. I also found one correctness bug in the `merge` method (it failed to extend the given snapshot with "unbound" for any missing symbols, meaning we would just lose the fact that the symbol could be unbound in the merged-in path), and two efficiency bugs (if one of the ranges to merge is empty, we can just use the other one, no need for copies, and if the ranges are overlapping -- which can occur with nested branches -- we can still just merge them with no copies), and fixed all three.
This commit is contained in:
parent
8f1be31289
commit
811f78d94d
4 changed files with 156 additions and 57 deletions
|
@ -143,7 +143,7 @@ impl<'db> SemanticIndexBuilder<'db> {
|
|||
self.current_use_def_map().restore(state);
|
||||
}
|
||||
|
||||
fn flow_merge(&mut self, state: FlowSnapshot) {
|
||||
fn flow_merge(&mut self, state: &FlowSnapshot) {
|
||||
self.current_use_def_map().merge(state);
|
||||
}
|
||||
|
||||
|
@ -393,27 +393,27 @@ where
|
|||
self.visit_expr(&node.test);
|
||||
let pre_if = self.flow_snapshot();
|
||||
self.visit_body(&node.body);
|
||||
let mut last_clause_is_else = false;
|
||||
let mut post_clauses: Vec<FlowSnapshot> = vec![self.flow_snapshot()];
|
||||
let mut post_clauses: Vec<FlowSnapshot> = vec![];
|
||||
for clause in &node.elif_else_clauses {
|
||||
// we can only take an elif/else clause if none of the previous ones were taken
|
||||
// snapshot after every block except the last; the last one will just become
|
||||
// the state that we merge the other snapshots into
|
||||
post_clauses.push(self.flow_snapshot());
|
||||
// we can only take an elif/else branch if none of the previous ones were
|
||||
// taken, so the block entry state is always `pre_if`
|
||||
self.flow_restore(pre_if.clone());
|
||||
self.visit_elif_else_clause(clause);
|
||||
post_clauses.push(self.flow_snapshot());
|
||||
if clause.test.is_none() {
|
||||
last_clause_is_else = true;
|
||||
}
|
||||
}
|
||||
let mut post_clause_iter = post_clauses.into_iter();
|
||||
if last_clause_is_else {
|
||||
// if the last clause was an else, the pre_if state can't directly reach the
|
||||
// post-state; we must enter one of the clauses.
|
||||
self.flow_restore(post_clause_iter.next().unwrap());
|
||||
} else {
|
||||
self.flow_restore(pre_if);
|
||||
for post_clause_state in post_clauses {
|
||||
self.flow_merge(&post_clause_state);
|
||||
}
|
||||
for post_clause_state in post_clause_iter {
|
||||
self.flow_merge(post_clause_state);
|
||||
let has_else = node
|
||||
.elif_else_clauses
|
||||
.last()
|
||||
.is_some_and(|clause| clause.test.is_none());
|
||||
if !has_else {
|
||||
// if there's no else clause, then it's possible we took none of the branches,
|
||||
// and the pre_if state can reach here
|
||||
self.flow_merge(&pre_if);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -485,7 +485,7 @@ where
|
|||
let post_body = self.flow_snapshot();
|
||||
self.flow_restore(pre_if);
|
||||
self.visit_expr(orelse);
|
||||
self.flow_merge(post_body);
|
||||
self.flow_merge(&post_body);
|
||||
}
|
||||
_ => {
|
||||
walk_expr(self, expr);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue