Skip to content

Commit 4df840e

Browse files
committed
feat(semantic/cfg): propagate unreachable edges through subgraphs.
1 parent dadc62c commit 4df840e

22 files changed

+98
-60
lines changed

crates/oxc_semantic/examples/cfg.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use oxc_allocator::Allocator;
55
use oxc_parser::Parser;
66
use oxc_semantic::{DebugDot, DisplayDot, EdgeType, SemanticBuilder};
77
use oxc_span::SourceType;
8-
use petgraph::dot::{Config, Dot};
8+
use petgraph::{
9+
dot::{Config, Dot},
10+
visit::EdgeRef,
11+
};
912

1013
// Instruction:
1114
// 1. create a `test.js`,
@@ -91,8 +94,10 @@ fn main() -> std::io::Result<()> {
9194
&|_graph, edge| {
9295
let weight = edge.weight();
9396
let label = format!("label = \"{weight:?}\"");
94-
if matches!(weight, EdgeType::Unreachable) {
95-
format!("{label}, style = \"dotted\"")
97+
if matches!(weight, EdgeType::Unreachable)
98+
|| semantic.semantic.cfg().basic_block(edge.source()).unreachable
99+
{
100+
format!("{label}, style = \"dotted\" ")
96101
} else {
97102
label
98103
}

crates/oxc_semantic/src/control_flow/builder/mod.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::ReturnInstructionKind;
44
use context::Ctx;
55

66
pub use context::{CtxCursor, CtxFlags};
7+
use petgraph::Direction;
78

89
use super::{
910
AstNodeId, BasicBlock, BasicBlockId, CompactStr, ControlFlowGraph, EdgeType, ErrorEdgeKind,
@@ -56,6 +57,17 @@ impl<'a> ControlFlowGraphBuilder<'a> {
5657
self.basic_block_mut(self.current_node_ix)
5758
}
5859

60+
/// # Panics
61+
pub fn basic_block(&self, basic_block: BasicBlockId) -> &BasicBlock {
62+
let idx = *self
63+
.graph
64+
.node_weight(basic_block)
65+
.expect("expected `self.current_node_ix` to be a valid node index in self.graph");
66+
self.basic_blocks
67+
.get(idx)
68+
.expect("expected `self.current_node_ix` to be a valid node index in self.graph")
69+
}
70+
5971
/// # Panics
6072
pub fn basic_block_mut(&mut self, basic_block: BasicBlockId) -> &mut BasicBlock {
6173
let idx = *self
@@ -99,6 +111,18 @@ impl<'a> ControlFlowGraphBuilder<'a> {
99111
}
100112

101113
pub fn add_edge(&mut self, a: BasicBlockId, b: BasicBlockId, weight: EdgeType) {
114+
if self.basic_block(a).unreachable {
115+
if self.graph.edges_directed(b, Direction::Incoming).count() == 0 {
116+
self.basic_block_mut(b).unreachable = true;
117+
}
118+
} else if !self
119+
.basic_block(b)
120+
.instructions()
121+
.iter()
122+
.any(|it| matches!(it, Instruction { kind: InstructionKind::Unreachable, .. }))
123+
{
124+
self.basic_block_mut(b).unreachable = false;
125+
}
102126
self.graph.add_edge(a, b, weight);
103127
}
104128

@@ -193,12 +217,13 @@ impl<'a> ControlFlowGraphBuilder<'a> {
193217
pub fn append_unreachable(&mut self) {
194218
let current_node_ix = self.current_node_ix;
195219
let basic_block_with_unreachable_graph_ix = self.new_basic_block_normal();
220+
self.push_instruction(InstructionKind::Unreachable, None);
221+
self.current_basic_block().unreachable = true;
196222
self.add_edge(
197223
current_node_ix,
198224
basic_block_with_unreachable_graph_ix,
199225
EdgeType::Unreachable,
200226
);
201-
self.push_instruction(InstructionKind::Unreachable, None);
202227
}
203228

204229
#[inline]

crates/oxc_semantic/src/control_flow/dot.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use oxc_ast::{
33
AstKind,
44
};
55
use oxc_syntax::node::AstNodeId;
6-
use petgraph::dot::{Config, Dot};
6+
use petgraph::{
7+
dot::{Config, Dot},
8+
visit::EdgeRef,
9+
};
710

811
use crate::{
912
AstNode, AstNodes, BasicBlock, ControlFlowGraph, EdgeType, Instruction, InstructionKind,
@@ -56,7 +59,9 @@ impl DisplayDot for ControlFlowGraph {
5659
&|_graph, edge| {
5760
let weight = edge.weight();
5861
let label = format!("label = \"{weight:?}\" ");
59-
if matches!(weight, EdgeType::Unreachable) {
62+
if matches!(weight, EdgeType::Unreachable)
63+
|| self.basic_block(edge.source()).unreachable
64+
{
6065
format!("{label}, style = \"dotted\" ")
6166
} else {
6267
label
@@ -115,7 +120,9 @@ impl DebugDot for ControlFlowGraph {
115120
&|_graph, edge| {
116121
let weight = edge.weight();
117122
let label = format!("label = \"{weight:?}\" ");
118-
if matches!(weight, EdgeType::Unreachable) {
123+
if matches!(weight, EdgeType::Unreachable)
124+
|| self.basic_block(edge.source()).unreachable
125+
{
119126
format!("{label}, style = \"dotted\" ")
120127
} else {
121128
label

crates/oxc_semantic/src/control_flow/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,12 @@ pub enum CallType {
116116
#[derive(Debug)]
117117
pub struct BasicBlock {
118118
pub instructions: Vec<Instruction>,
119+
pub unreachable: bool,
119120
}
120121

121122
impl BasicBlock {
122123
fn new() -> Self {
123-
BasicBlock { instructions: Vec::new() }
124+
BasicBlock { instructions: Vec::new(), unreachable: false }
124125
}
125126

126127
pub fn instructions(&self) -> &Vec<Instruction> {

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@break_from_a_label_in_global_scope.js-2.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ digraph {
99
2 [ label = "unreachable" ]
1010
3 [ label = "" ]
1111
1 -> 0 [ label = "Error(Implicit)" ]
12-
2 -> 0 [ label = "Error(Implicit)" ]
12+
2 -> 0 [ label = "Error(Implicit)" , style = "dotted" ]
1313
1 -> 2 [ label = "Unreachable" , style = "dotted" ]
1414
3 -> 0 [ label = "Error(Implicit)" ]
15-
2 -> 3 [ label = "Normal" ]
15+
2 -> 3 [ label = "Normal" , style = "dotted" ]
1616
1 -> 3 [ label = "Jump" ]
1717
}

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@class_extend_super.js-2.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ digraph {
1313
1 -> 0 [ label = "Error(Implicit)" ]
1414
3 -> 2 [ label = "Error(Implicit)" ]
1515
1 -> 3 [ label = "NewFunction" ]
16-
4 -> 2 [ label = "Error(Implicit)" ]
16+
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
1717
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
1818
5 -> 0 [ label = "Error(Implicit)" ]
1919
1 -> 5 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@do_while_break.js-2.snap

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ digraph {
2525
6 -> 2 [ label = "Error(Implicit)" ]
2626
4 -> 6 [ label = "Normal" ]
2727
7 -> 2 [ label = "Error(Implicit)" ]
28-
8 -> 2 [ label = "Error(Implicit)" ]
28+
8 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
2929
7 -> 8 [ label = "Unreachable" , style = "dotted" ]
30-
9 -> 2 [ label = "Error(Implicit)" ]
30+
9 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3131
10 -> 2 [ label = "Error(Implicit)" ]
3232
6 -> 7 [ label = "Normal" ]
33-
8 -> 9 [ label = "Normal" ]
34-
9 -> 10 [ label = "Normal" ]
35-
9 -> 7 [ label = "Backedge" ]
33+
8 -> 9 [ label = "Normal" , style = "dotted" ]
34+
9 -> 10 [ label = "Normal" , style = "dotted" ]
35+
9 -> 7 [ label = "Backedge" , style = "dotted" ]
3636
7 -> 10 [ label = "Jump" ]
3737
11 -> 2 [ label = "Error(Implicit)" ]
3838
3 -> 5 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@fn_return_obj_expr_with_computed_key.js-2.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ digraph {
1313
1 -> 0 [ label = "Error(Implicit)" ]
1414
3 -> 2 [ label = "Error(Implicit)" ]
1515
1 -> 3 [ label = "NewFunction" ]
16-
4 -> 2 [ label = "Error(Implicit)" ]
16+
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
1717
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
1818
5 -> 0 [ label = "Error(Implicit)" ]
1919
1 -> 5 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@if_else.js-2.snap

+6-6
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,18 @@ digraph {
2828
4 -> 6 [ label = "Normal" ]
2929
5 -> 6 [ label = "Normal" ]
3030
7 -> 2 [ label = "Error(Implicit)" ]
31-
8 -> 2 [ label = "Error(Implicit)" ]
31+
8 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3232
7 -> 8 [ label = "Unreachable" , style = "dotted" ]
3333
9 -> 2 [ label = "Error(Implicit)" ]
34-
10 -> 2 [ label = "Error(Implicit)" ]
34+
10 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3535
9 -> 10 [ label = "Unreachable" , style = "dotted" ]
36-
11 -> 2 [ label = "Error(Implicit)" ]
36+
11 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3737
3 -> 4 [ label = "Normal" ]
38-
8 -> 11 [ label = "Normal" ]
38+
8 -> 11 [ label = "Normal" , style = "dotted" ]
3939
6 -> 7 [ label = "Jump" ]
4040
3 -> 9 [ label = "Normal" ]
41-
10 -> 11 [ label = "Normal" ]
42-
12 -> 2 [ label = "Error(Implicit)" ]
41+
10 -> 11 [ label = "Normal" , style = "dotted" ]
42+
12 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
4343
11 -> 12 [ label = "Unreachable" , style = "dotted" ]
4444
13 -> 0 [ label = "Error(Implicit)" ]
4545
1 -> 13 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@if_stmt_in_for_in.js-2.snap

+4-4
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,21 @@ digraph {
3030
6 -> 2 [ label = "Error(Implicit)" ]
3131
7 -> 2 [ label = "Error(Implicit)" ]
3232
8 -> 2 [ label = "Error(Implicit)" ]
33-
9 -> 2 [ label = "Error(Implicit)" ]
33+
9 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3434
8 -> 9 [ label = "Unreachable" , style = "dotted" ]
3535
10 -> 2 [ label = "Error(Implicit)" ]
3636
11 -> 2 [ label = "Error(Implicit)" ]
3737
12 -> 2 [ label = "Error(Implicit)" ]
38-
13 -> 2 [ label = "Error(Implicit)" ]
38+
13 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3939
12 -> 13 [ label = "Unreachable" , style = "dotted" ]
4040
14 -> 2 [ label = "Error(Implicit)" ]
4141
10 -> 11 [ label = "Normal" ]
42-
13 -> 14 [ label = "Normal" ]
42+
13 -> 14 [ label = "Normal" , style = "dotted" ]
4343
11 -> 12 [ label = "Jump" ]
4444
10 -> 14 [ label = "Normal" ]
4545
15 -> 2 [ label = "Error(Implicit)" ]
4646
6 -> 7 [ label = "Normal" ]
47-
9 -> 15 [ label = "Normal" ]
47+
9 -> 15 [ label = "Normal" , style = "dotted" ]
4848
7 -> 8 [ label = "Jump" ]
4949
6 -> 10 [ label = "Normal" ]
5050
14 -> 15 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@labeled_block_break.js-2.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ digraph {
2121
2 -> 4 [ label = "Normal" ]
2222
5 -> 0 [ label = "Error(Implicit)" ]
2323
6 -> 0 [ label = "Error(Implicit)" ]
24-
7 -> 0 [ label = "Error(Implicit)" ]
24+
7 -> 0 [ label = "Error(Implicit)" , style = "dotted" ]
2525
6 -> 7 [ label = "Unreachable" , style = "dotted" ]
2626
8 -> 0 [ label = "Error(Implicit)" ]
2727
4 -> 5 [ label = "Normal" ]
28-
7 -> 8 [ label = "Normal" ]
28+
7 -> 8 [ label = "Normal" , style = "dotted" ]
2929
5 -> 6 [ label = "Jump" ]
3030
4 -> 8 [ label = "Normal" ]
3131
9 -> 0 [ label = "Error(Implicit)" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@labelled_try_break.js-2.snap

+7-7
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,20 @@ digraph {
2222
1 -> 3 [ label = "NewFunction" ]
2323
5 -> 2 [ label = "Error(Implicit)" ]
2424
5 -> 4 [ label = "Finalize" ]
25-
6 -> 2 [ label = "Error(Implicit)" ]
26-
6 -> 4 [ label = "Finalize" ]
25+
6 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
26+
6 -> 4 [ label = "Finalize" , style = "dotted" ]
2727
5 -> 6 [ label = "Unreachable" , style = "dotted" ]
2828
7 -> 2 [ label = "Error(Implicit)" ]
2929
4 -> 7 [ label = "Normal" ]
30-
8 -> 2 [ label = "Error(Implicit)" ]
30+
8 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3131
7 -> 8 [ label = "Unreachable" , style = "dotted" ]
32-
9 -> 2 [ label = "Error(Implicit)" ]
32+
9 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3333
3 -> 5 [ label = "Normal" ]
34-
8 -> 9 [ label = "Join" ]
34+
8 -> 9 [ label = "Join" , style = "dotted" ]
3535
10 -> 2 [ label = "Error(Implicit)" ]
36-
9 -> 10 [ label = "Normal" ]
36+
9 -> 10 [ label = "Normal" , style = "dotted" ]
3737
7 -> 10 [ label = "Jump" ]
38-
11 -> 2 [ label = "Error(Implicit)" ]
38+
11 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
3939
10 -> 11 [ label = "Unreachable" , style = "dotted" ]
4040
12 -> 0 [ label = "Error(Implicit)" ]
4141
1 -> 12 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@member_access_with_numbered_index.js-2.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ digraph {
1414
1 -> 0 [ label = "Error(Implicit)" ]
1515
3 -> 2 [ label = "Error(Implicit)" ]
1616
1 -> 3 [ label = "NewFunction" ]
17-
4 -> 2 [ label = "Error(Implicit)" ]
17+
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
1818
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
19-
5 -> 2 [ label = "Error(Implicit)" ]
19+
5 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
2020
4 -> 5 [ label = "Unreachable" , style = "dotted" ]
2121
6 -> 0 [ label = "Error(Implicit)" ]
2222
1 -> 6 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@member_access_with_unreachable.js-2.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ digraph {
1414
1 -> 0 [ label = "Error(Implicit)" ]
1515
3 -> 2 [ label = "Error(Implicit)" ]
1616
1 -> 3 [ label = "NewFunction" ]
17-
4 -> 2 [ label = "Error(Implicit)" ]
17+
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
1818
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
19-
5 -> 2 [ label = "Error(Implicit)" ]
19+
5 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
2020
4 -> 5 [ label = "Unreachable" , style = "dotted" ]
2121
6 -> 0 [ label = "Error(Implicit)" ]
2222
1 -> 6 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@nested_computed_member_expression.js-2.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ digraph {
1313
1 -> 0 [ label = "Error(Implicit)" ]
1414
3 -> 2 [ label = "Error(Implicit)" ]
1515
1 -> 3 [ label = "NewFunction" ]
16-
4 -> 2 [ label = "Error(Implicit)" ]
16+
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
1717
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
1818
5 -> 0 [ label = "Error(Implicit)" ]
1919
1 -> 5 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@private_in.js-2.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ digraph {
1313
1 -> 0 [ label = "Error(Implicit)" ]
1414
3 -> 2 [ label = "Error(Implicit)" ]
1515
1 -> 3 [ label = "NewFunction" ]
16-
4 -> 2 [ label = "Error(Implicit)" ]
16+
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
1717
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
1818
5 -> 0 [ label = "Error(Implicit)" ]
1919
1 -> 5 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@simple_spread.js-2.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ digraph {
1313
1 -> 0 [ label = "Error(Implicit)" ]
1414
3 -> 2 [ label = "Error(Implicit)" ]
1515
1 -> 3 [ label = "NewFunction" ]
16-
4 -> 2 [ label = "Error(Implicit)" ]
16+
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
1717
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
1818
5 -> 0 [ label = "Error(Implicit)" ]
1919
1 -> 5 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@spread_in_a_spread.js-2.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ digraph {
1313
1 -> 0 [ label = "Error(Implicit)" ]
1414
3 -> 2 [ label = "Error(Implicit)" ]
1515
1 -> 3 [ label = "NewFunction" ]
16-
4 -> 2 [ label = "Error(Implicit)" ]
16+
4 -> 2 [ label = "Error(Implicit)" , style = "dotted" ]
1717
3 -> 4 [ label = "Unreachable" , style = "dotted" ]
1818
5 -> 0 [ label = "Error(Implicit)" ]
1919
1 -> 5 [ label = "Normal" ]

crates/oxc_semantic/tests/integration/snapshots/integration__cfg__cfg_files@switch_in_while.js-2.snap

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ digraph {
2121
4 -> 0 [ label = "Error(Implicit)" ]
2222
5 -> 0 [ label = "Error(Implicit)" ]
2323
4 -> 5 [ label = "Normal" ]
24-
6 -> 0 [ label = "Error(Implicit)" ]
24+
6 -> 0 [ label = "Error(Implicit)" , style = "dotted" ]
2525
5 -> 6 [ label = "Unreachable" , style = "dotted" ]
2626
7 -> 0 [ label = "Error(Implicit)" ]
2727
8 -> 0 [ label = "Error(Implicit)" ]
2828
7 -> 8 [ label = "Normal" ]
2929
4 -> 7 [ label = "Normal" ]
30-
6 -> 7 [ label = "Normal" ]
30+
6 -> 7 [ label = "Normal" , style = "dotted" ]
3131
3 -> 4 [ label = "Normal" ]
3232
3 -> 7 [ label = "Normal" ]
3333
9 -> 0 [ label = "Error(Implicit)" ]

0 commit comments

Comments
 (0)