mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(semantic/cfg): connect for update exit to loop test (#23791)
## Summary Correct the CFG generated for `for` statements whose update expression spans multiple basic blocks, such as conditional or logical expressions. This addresses the malformed CFG underlying #23789. ## Problem `update_graph_ix` identifies the entry of the update expression. After visiting the expression, `cfg.current_node_ix` identifies its exit block. Previously, the loop backedge to the test originated from the update entry. This allowed control flow to bypass the update expression and left its exit block disconnected. ### Before ```mermaid flowchart LR Test["Loop test"] -->|truthy| Body["Loop body"] Test -->|falsy| After["After loop"] Body -->|backedge| Entry["Update entry"] Entry --> Update["Update expression blocks"] Update --> Exit["Update exit"] Entry -->|backedge| Test ``` ### After ```mermaid flowchart LR Test["Loop test"] -->|truthy| Body["Loop body"] Test -->|falsy| After["After loop"] Body -->|backedge| Entry["Update entry"] Entry --> Update["Update expression blocks"] Update --> Exit["Update exit"] Exit -->|backedge| Test ``` ## Implementation Record `cfg.current_node_ix` after visiting the update expression as `after_update_graph_ix`. The backedge to the loop test now originates from that exit block. The loop body backedge and `continue` target still point to `update_graph_ix`, ensuring the complete update expression runs before the next test. A dedicated CFG fixture covers a conditional update expression: ```js forward ? index++ : index-- ```
This commit is contained in:
1 parent
d3e92d5258
commit
869ac200a4
3 files changed
+91
-3
No files matched your search
@@ -1333,10 +1333,11 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
||||
|
||||
/* cfg */
|
||||
#[cfg(feature = "cfg")]
|
||||
let before_body_graph_ix = control_flow!(self, |cfg| {
|
||||
let (after_update_graph_ix, before_body_graph_ix) = control_flow!(self, |cfg| {
|
||||
let after_update_graph_ix = cfg.current_node_ix;
|
||||
let before_body_graph_ix = cfg.new_basic_block_normal();
|
||||
cfg.ctx(None).default().allow_break().allow_continue();
|
||||
before_body_graph_ix
|
||||
(after_update_graph_ix, before_body_graph_ix)
|
||||
});
|
||||
/* cfg */
|
||||
|
||||
@@ -1349,7 +1350,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
|
||||
cfg.add_edge(before_for_graph_ix, test_graph_ix, EdgeType::Normal);
|
||||
cfg.add_edge(after_test_graph_ix, before_body_graph_ix, EdgeType::Jump);
|
||||
cfg.add_edge(after_body_graph_ix, update_graph_ix, EdgeType::Backedge);
|
||||
cfg.add_edge(update_graph_ix, test_graph_ix, EdgeType::Backedge);
|
||||
cfg.add_edge(after_update_graph_ix, test_graph_ix, EdgeType::Backedge);
|
||||
cfg.add_edge(after_test_graph_ix, after_for_stmt, EdgeType::Normal);
|
||||
|
||||
cfg.ctx(None)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
for (let index = 0; index < length; forward ? index++ : index--) {
|
||||
consume(index);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
source: crates/oxc_semantic/tests/integration/cfg.rs
|
||||
expression: snapshot
|
||||
input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/for_conditional_update.js
|
||||
---
|
||||
bb0: {
|
||||
|
||||
}
|
||||
|
||||
bb1: {
|
||||
statement
|
||||
statement
|
||||
}
|
||||
|
||||
bb2: {
|
||||
condition
|
||||
}
|
||||
|
||||
bb3: {
|
||||
|
||||
}
|
||||
|
||||
bb4: {
|
||||
condition
|
||||
}
|
||||
|
||||
bb5: {
|
||||
|
||||
}
|
||||
|
||||
bb6: {
|
||||
|
||||
}
|
||||
|
||||
bb7: {
|
||||
|
||||
}
|
||||
|
||||
bb8: {
|
||||
statement
|
||||
statement
|
||||
}
|
||||
|
||||
bb9: {
|
||||
|
||||
}
|
||||
|
||||
digraph {
|
||||
0 [ label = "bb0" shape = box]
|
||||
1 [ label = "bb1
|
||||
ForStatement
|
||||
VariableDeclaration" shape = box]
|
||||
2 [ label = "bb2
|
||||
Condition(BinaryExpression(<))" shape = box]
|
||||
3 [ label = "bb3" shape = box]
|
||||
4 [ label = "bb4
|
||||
Condition(IdentifierReference(forward))" shape = box]
|
||||
5 [ label = "bb5" shape = box]
|
||||
6 [ label = "bb6" shape = box]
|
||||
7 [ label = "bb7" shape = box]
|
||||
8 [ label = "bb8
|
||||
BlockStatement
|
||||
ExpressionStatement" shape = box]
|
||||
9 [ label = "bb9" shape = box]
|
||||
1 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
2 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
3 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
4 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
5 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
6 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
7 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
3 -> 4 [ label="Normal"]
|
||||
5 -> 7 [ label="Normal"]
|
||||
4 -> 5 [ label="Jump", color=green]
|
||||
4 -> 6 [ label="Normal"]
|
||||
6 -> 7 [ label="Normal"]
|
||||
8 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
9 -> 0 [ label="Error(Implicit)", color=red, style=dashed]
|
||||
1 -> 2 [ label="Normal"]
|
||||
2 -> 8 [ label="Jump", color=green]
|
||||
8 -> 3 [ label="Backedge", color=grey]
|
||||
7 -> 2 [ label="Backedge", color=grey]
|
||||
2 -> 9 [ label="Normal"]
|
||||
}
|
||||
Reference in new issue
Block a user