Skip to content

Commit e0649aa

Browse files
committed
refactor(semantic/cfg): alias petgraph's NodeIndex as BasicBlockId.
1 parent 40ab95b commit e0649aa

File tree

10 files changed

+77
-74
lines changed

10 files changed

+77
-74
lines changed

crates/oxc_linter/src/rules/eslint/getter_return.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ impl GetterReturn {
182182

183183
let output = neighbors_filtered_by_edge_weight(
184184
&cfg.graph,
185-
node.cfg_ix(),
185+
node.cfg_id(),
186186
&|edge| match edge {
187187
EdgeType::Normal => None,
188188
// We don't need to handle backedges because we would have already visited
@@ -229,7 +229,7 @@ impl GetterReturn {
229229
}
230230

231231
// Scan through the values in this basic block.
232-
for entry in cfg.basic_block_by_index(*basic_block_id) {
232+
for entry in cfg.basic_block(*basic_block_id) {
233233
match entry {
234234
// If the element is an assignment.
235235
//

crates/oxc_linter/src/rules/eslint/no_this_before_super.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ use oxc_ast::{
77
AstKind,
88
};
99
use oxc_macros::declare_oxc_lint;
10-
use oxc_semantic::{
11-
petgraph::stable_graph::NodeIndex, pg::neighbors_filtered_by_edge_weight, AstNodeId, EdgeType,
12-
};
10+
use oxc_semantic::{pg::neighbors_filtered_by_edge_weight, AstNodeId, BasicBlockId, EdgeType};
1311
use oxc_span::{GetSpan, Span};
1412

1513
use crate::{context::LintContext, rule::Rule, AstNode};
@@ -59,8 +57,8 @@ impl Rule for NoThisBeforeSuper {
5957

6058
// first pass -> find super calls and local violations
6159
let mut wanted_nodes = Vec::new();
62-
let mut basic_blocks_with_super_called = HashSet::<NodeIndex>::new();
63-
let mut basic_blocks_with_local_violations = HashMap::<NodeIndex, Vec<AstNodeId>>::new();
60+
let mut basic_blocks_with_super_called = HashSet::<BasicBlockId>::new();
61+
let mut basic_blocks_with_local_violations = HashMap::<BasicBlockId, Vec<AstNodeId>>::new();
6462
for node in semantic.nodes().iter() {
6563
match node.kind() {
6664
AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) => {
@@ -69,7 +67,7 @@ impl Rule for NoThisBeforeSuper {
6967
}
7068
}
7169
AstKind::Super(_) => {
72-
let basic_block_id = node.cfg_ix();
70+
let basic_block_id = node.cfg_id();
7371
if let Some(parent) = semantic.nodes().parent_node(node.id()) {
7472
if let AstKind::CallExpression(_) = parent.kind() {
7573
// Note: we don't need to worry about also having invalid
@@ -86,7 +84,7 @@ impl Rule for NoThisBeforeSuper {
8684
}
8785
}
8886
AstKind::ThisExpression(_) => {
89-
let basic_block_id = node.cfg_ix();
87+
let basic_block_id = node.cfg_id();
9088
if !basic_blocks_with_super_called.contains(&basic_block_id) {
9189
basic_blocks_with_local_violations
9290
.entry(basic_block_id)
@@ -103,7 +101,7 @@ impl Rule for NoThisBeforeSuper {
103101
for node in wanted_nodes {
104102
let output = neighbors_filtered_by_edge_weight(
105103
&cfg.graph,
106-
node.cfg_ix(),
104+
node.cfg_id(),
107105
&|edge| match edge {
108106
EdgeType::Normal => None,
109107
EdgeType::Backedge | EdgeType::NewFunction => {

crates/oxc_linter/src/rules/react/require_render_return.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ fn contains_return_statement<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> b
9494
let cfg = ctx.semantic().cfg();
9595
let state = neighbors_filtered_by_edge_weight(
9696
&cfg.graph,
97-
node.cfg_ix(),
97+
node.cfg_id(),
9898
&|edge| match edge {
9999
// We only care about normal edges having a return statement.
100100
EdgeType::Normal => None,
@@ -109,7 +109,7 @@ fn contains_return_statement<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> b
109109
}
110110
}
111111

112-
for entry in cfg.basic_block_by_index(*basic_block_id) {
112+
for entry in cfg.basic_block(*basic_block_id) {
113113
if let BasicBlockElement::Assignment(to_reg, val) = entry {
114114
if matches!(to_reg, Register::Return)
115115
&& matches!(val, AssignmentValue::NotImplicitUndefined)

crates/oxc_linter/src/rules/react/rules_of_hooks.rs

+24-24
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use oxc_ast::{
55
};
66
use oxc_macros::declare_oxc_lint;
77
use oxc_semantic::{
8-
petgraph::{self, graph::NodeIndex},
8+
petgraph::{self},
99
pg::neighbors_filtered_by_edge_weight,
10-
AstNodeId, AstNodes, BasicBlockElement, EdgeType, Register,
10+
AstNodeId, AstNodes, BasicBlockElement, BasicBlockId, EdgeType, Register,
1111
};
1212
use oxc_span::{Atom, CompactStr};
1313
use oxc_syntax::operator::AssignmentOperator;
@@ -220,18 +220,18 @@ impl Rule for RulesOfHooks {
220220
return;
221221
}
222222

223-
let node_cfg_ix = node.cfg_ix();
224-
let func_cfg_ix = parent_func.cfg_ix();
223+
let node_cfg_id = node.cfg_id();
224+
let func_cfg_id = parent_func.cfg_id();
225225

226226
// there is no branch between us and our parent function
227-
if node_cfg_ix == func_cfg_ix {
227+
if node_cfg_id == func_cfg_id {
228228
return;
229229
}
230230

231231
if !petgraph::algo::has_path_connecting(
232232
&semantic.cfg().graph,
233-
func_cfg_ix,
234-
node_cfg_ix,
233+
func_cfg_id,
234+
node_cfg_id,
235235
None,
236236
) {
237237
// There should always be a control flow path between a parent and child node.
@@ -246,8 +246,8 @@ impl Rule for RulesOfHooks {
246246
return ctx.diagnostic(diagnostics::loop_hook(span, hook_name));
247247
}
248248

249-
if self.is_conditional(ctx, func_cfg_ix, node_cfg_ix)
250-
|| self.breaks_early(ctx, func_cfg_ix, node_cfg_ix)
249+
if self.is_conditional(ctx, func_cfg_id, node_cfg_id)
250+
|| self.breaks_early(ctx, func_cfg_id, node_cfg_id)
251251
{
252252
#[allow(clippy::needless_return)]
253253
return ctx.diagnostic(diagnostics::conditional_hook(span, hook_name));
@@ -278,42 +278,42 @@ impl RulesOfHooks {
278278
fn is_conditional(
279279
&self,
280280
ctx: &LintContext,
281-
func_cfg_ix: NodeIndex,
282-
node_cfg_ix: NodeIndex,
281+
func_cfg_id: BasicBlockId,
282+
node_cfg_id: BasicBlockId,
283283
) -> bool {
284284
let graph = &ctx.semantic().cfg().graph;
285285
// All nodes should be reachable from our hook, Otherwise we have a conditional/branching flow.
286-
petgraph::algo::dijkstra(graph, func_cfg_ix, Some(node_cfg_ix), |e| match e.weight() {
286+
petgraph::algo::dijkstra(graph, func_cfg_id, Some(node_cfg_id), |e| match e.weight() {
287287
EdgeType::NewFunction => 1,
288288
EdgeType::Backedge | EdgeType::Normal => 0,
289289
})
290290
.into_iter()
291291
.filter(|(_, val)| *val == 0)
292-
.any(|(f, _)| !petgraph::algo::has_path_connecting(graph, f, node_cfg_ix, None))
292+
.any(|(f, _)| !petgraph::algo::has_path_connecting(graph, f, node_cfg_id, None))
293293
}
294294

295295
#[inline(always)]
296296
fn breaks_early(
297297
&self,
298298
ctx: &LintContext,
299-
func_cfg_ix: NodeIndex,
300-
node_cfg_ix: NodeIndex,
299+
func_cfg_id: BasicBlockId,
300+
node_cfg_id: BasicBlockId,
301301
) -> bool {
302302
let cfg = ctx.semantic().cfg();
303303
neighbors_filtered_by_edge_weight(
304304
&cfg.graph,
305-
func_cfg_ix,
305+
func_cfg_id,
306306
&|e| match e {
307307
EdgeType::Normal => None,
308308
EdgeType::Backedge | EdgeType::NewFunction => Some(State::default()),
309309
},
310-
&mut |ix: &NodeIndex, mut state: State| {
311-
if node_cfg_ix == *ix {
310+
&mut |id: &BasicBlockId, mut state: State| {
311+
if node_cfg_id == *id {
312312
return (state, false);
313313
}
314314

315315
let (push, keep_walking) = cfg
316-
.basic_block_by_index(*ix)
316+
.basic_block(*id)
317317
.iter()
318318
.fold_while((false, true), |acc, it| match it {
319319
BasicBlockElement::Break(_) => FoldWhile::Done((true, false)),
@@ -327,7 +327,7 @@ impl RulesOfHooks {
327327
.into_inner();
328328

329329
if push {
330-
state.0.push(*ix);
330+
state.0.push(*id);
331331
}
332332
(state, keep_walking)
333333
},
@@ -340,7 +340,7 @@ impl RulesOfHooks {
340340
}
341341

342342
#[derive(Debug, Default, Clone)]
343-
struct State(Vec<NodeIndex>);
343+
struct State(Vec<BasicBlockId>);
344344

345345
fn parent_func<'a>(nodes: &'a AstNodes<'a>, node: &AstNode) -> Option<&'a AstNode<'a>> {
346346
nodes.ancestors(node.id()).map(|id| nodes.get_node(id)).find(|it| it.kind().is_function_like())
@@ -380,10 +380,10 @@ fn is_somewhere_inside_component_or_hook(nodes: &AstNodes, node_id: AstNodeId) -
380380
},
381381
)
382382
})
383-
.any(|(ix, id)| {
384-
id.is_some_and(|name| {
383+
.any(|(id, ident)| {
384+
ident.is_some_and(|name| {
385385
is_react_component_or_hook_name(name.as_str())
386-
|| is_memo_or_forward_ref_callback(nodes, ix)
386+
|| is_memo_or_forward_ref_callback(nodes, id)
387387
})
388388
})
389389
}

crates/oxc_semantic/examples/cfg.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ fn main() -> std::io::Result<()> {
5454

5555
let mut ast_nodes_by_block = HashMap::<_, Vec<_>>::new();
5656
for node in semantic.semantic.nodes().iter() {
57-
let block = node.cfg_ix();
57+
let block = node.cfg_id();
5858
let block_ix = semantic.semantic.cfg().graph.node_weight(block).unwrap();
5959
ast_nodes_by_block.entry(*block_ix).or_default().push(node);
6060
}

crates/oxc_semantic/src/control_flow/builder.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use super::{
2-
AssignmentValue, AstNodeId, BasicBlockElement, CompactStr, ControlFlowGraph, EdgeType, Graph,
3-
NodeIndex, PreservedExpressionState, PreservedStatementState, Register,
2+
AssignmentValue, AstNodeId, BasicBlockElement, BasicBlockId, CompactStr, ControlFlowGraph,
3+
EdgeType, Graph, PreservedExpressionState, PreservedStatementState, Register,
44
StatementControlFlowType,
55
};
66

77
#[derive(Debug, Default)]
88
pub struct ControlFlowGraphBuilder {
9+
pub graph: Graph<usize, EdgeType>,
910
pub basic_blocks: Vec<Vec<BasicBlockElement>>,
11+
pub current_node_ix: BasicBlockId,
1012
// note: this should only land in the big box for all things that take arguments
1113
// ie: callexpression, arrayexpression, etc
1214
// todo: add assert that it is used every time?
@@ -22,16 +24,14 @@ pub struct ControlFlowGraphBuilder {
2224
// (start, tail, last_register_used)
2325
pub saved_stores: Vec<(Vec<BasicBlockElement>, Option<Register>)>,
2426
pub saved_store: Option<usize>,
25-
pub graph: Graph<usize, EdgeType>,
26-
pub current_node_ix: NodeIndex,
27-
pub basic_blocks_with_breaks: Vec<Vec<NodeIndex>>,
28-
pub basic_blocks_with_continues: Vec<Vec<NodeIndex>>,
27+
pub basic_blocks_with_breaks: Vec<Vec<BasicBlockId>>,
28+
pub basic_blocks_with_continues: Vec<Vec<BasicBlockId>>,
2929
// node indexes of the basic blocks of switch case conditions
30-
pub switch_case_conditions: Vec<Vec<NodeIndex>>,
30+
pub switch_case_conditions: Vec<Vec<BasicBlockId>>,
3131
pub next_label: Option<CompactStr>,
3232
pub label_to_ast_node_ix: Vec<(CompactStr, AstNodeId)>,
3333
pub ast_node_to_break_continue: Vec<(AstNodeId, usize, Option<usize>)>,
34-
pub after_throw_block: Option<NodeIndex>,
34+
pub after_throw_block: Option<BasicBlockId>,
3535
}
3636

3737
impl ControlFlowGraphBuilder {
@@ -51,7 +51,7 @@ impl ControlFlowGraphBuilder {
5151
}
5252

5353
#[must_use]
54-
pub fn new_basic_block_for_function(&mut self) -> NodeIndex {
54+
pub fn new_basic_block_for_function(&mut self) -> BasicBlockId {
5555
self.basic_blocks.push(Vec::new());
5656
let basic_block_id = self.basic_blocks.len() - 1;
5757
let graph_index = self.graph.add_node(basic_block_id);
@@ -66,7 +66,7 @@ impl ControlFlowGraphBuilder {
6666
}
6767

6868
#[must_use]
69-
pub fn new_basic_block(&mut self) -> NodeIndex {
69+
pub fn new_basic_block(&mut self) -> BasicBlockId {
7070
self.basic_blocks.push(Vec::new());
7171
let graph_index = self.graph.add_node(self.basic_blocks.len() - 1);
7272
self.current_node_ix = graph_index;
@@ -79,7 +79,7 @@ impl ControlFlowGraphBuilder {
7979
graph_index
8080
}
8181

82-
pub fn add_edge(&mut self, a: NodeIndex, b: NodeIndex, weight: EdgeType) {
82+
pub fn add_edge(&mut self, a: BasicBlockId, b: BasicBlockId, weight: EdgeType) {
8383
self.graph.add_edge(a, b, weight);
8484
}
8585

@@ -202,8 +202,8 @@ impl ControlFlowGraphBuilder {
202202
&mut self,
203203
preserved_state: &PreservedStatementState,
204204
id: AstNodeId,
205-
break_jump_position: NodeIndex<u32>,
206-
continue_jump_position: Option<NodeIndex<u32>>,
205+
break_jump_position: BasicBlockId,
206+
continue_jump_position: Option<BasicBlockId>,
207207
) {
208208
let basic_blocks_with_breaks = self
209209
.basic_blocks_with_breaks

crates/oxc_semantic/src/control_flow/mod.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use crate::AstNodeId;
1010

1111
pub use builder::ControlFlowGraphBuilder;
1212

13+
pub type BasicBlockId = NodeIndex;
14+
1315
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1416
pub enum Register {
1517
Index(u32),
@@ -128,17 +130,15 @@ pub struct ControlFlowGraph {
128130

129131
impl ControlFlowGraph {
130132
/// # Panics
131-
pub fn basic_block_by_index(&self, index: NodeIndex) -> &Vec<BasicBlockElement> {
132-
let idx =
133-
*self.graph.node_weight(index).expect("expected a valid node index in self.graph");
134-
self.basic_blocks.get(idx).expect("expected a valid node index in self.basic_blocks")
133+
pub fn basic_block(&self, id: BasicBlockId) -> &Vec<BasicBlockElement> {
134+
let ix = *self.graph.node_weight(id).expect("expected a valid node id in self.graph");
135+
self.basic_blocks.get(ix).expect("expected a valid node id in self.basic_blocks")
135136
}
136137

137138
/// # Panics
138-
pub fn basic_block_by_index_mut(&mut self, index: NodeIndex) -> &mut Vec<BasicBlockElement> {
139-
let idx =
140-
*self.graph.node_weight(index).expect("expected a valid node index in self.graph");
141-
self.basic_blocks.get_mut(idx).expect("expected a valid node index in self.basic_blocks")
139+
pub fn basic_block_mut(&mut self, id: BasicBlockId) -> &mut Vec<BasicBlockElement> {
140+
let ix = *self.graph.node_weight(id).expect("expected a valid node id in self.graph");
141+
self.basic_blocks.get_mut(ix).expect("expected a valid node id in self.basic_blocks")
142142
}
143143
}
144144

crates/oxc_semantic/src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ use rustc_hash::FxHashSet;
3131

3232
pub use crate::{
3333
control_flow::{
34-
print_basic_block, AssignmentValue, BasicBlockElement, BinaryAssignmentValue, BinaryOp,
35-
CallType, CalleeWithArgumentsAssignmentValue, CollectionAssignmentValue, ControlFlowGraph,
36-
EdgeType, ObjectPropertyAccessAssignmentValue, Register, UnaryExpressioneAssignmentValue,
37-
UpdateAssignmentValue,
34+
print_basic_block, AssignmentValue, BasicBlockElement, BasicBlockId, BinaryAssignmentValue,
35+
BinaryOp, CallType, CalleeWithArgumentsAssignmentValue, CollectionAssignmentValue,
36+
ControlFlowGraph, EdgeType, ObjectPropertyAccessAssignmentValue, Register,
37+
UnaryExpressioneAssignmentValue, UpdateAssignmentValue,
3838
},
3939
node::{AstNode, AstNodeId, AstNodes},
4040
reference::{Reference, ReferenceFlag, ReferenceId},

crates/oxc_semantic/src/node.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
use petgraph::stable_graph::NodeIndex;
2-
31
use oxc_ast::AstKind;
42
use oxc_index::IndexVec;
53

6-
use crate::scope::ScopeId;
4+
use crate::{control_flow::BasicBlockId, scope::ScopeId};
75

86
pub use oxc_syntax::node::{AstNodeId, NodeFlags};
97

@@ -17,23 +15,28 @@ pub struct AstNode<'a> {
1715
/// Associated Scope (initialized by binding)
1816
scope_id: ScopeId,
1917

20-
/// Associated NodeIndex in CFG (initialized by control_flow)
21-
cfg_ix: NodeIndex,
18+
/// Associated `BasicBlockId` in CFG (initialized by control_flow)
19+
cfg_id: BasicBlockId,
2220

2321
flags: NodeFlags,
2422
}
2523

2624
impl<'a> AstNode<'a> {
27-
pub fn new(kind: AstKind<'a>, scope_id: ScopeId, cfg_ix: NodeIndex, flags: NodeFlags) -> Self {
28-
Self { id: AstNodeId::new(0), kind, cfg_ix, scope_id, flags }
25+
pub fn new(
26+
kind: AstKind<'a>,
27+
scope_id: ScopeId,
28+
cfg_id: BasicBlockId,
29+
flags: NodeFlags,
30+
) -> Self {
31+
Self { id: AstNodeId::new(0), kind, cfg_id, scope_id, flags }
2932
}
3033

3134
pub fn id(&self) -> AstNodeId {
3235
self.id
3336
}
3437

35-
pub fn cfg_ix(&self) -> NodeIndex {
36-
self.cfg_ix
38+
pub fn cfg_id(&self) -> BasicBlockId {
39+
self.cfg_id
3740
}
3841

3942
pub fn kind(&self) -> AstKind<'a> {

0 commit comments

Comments
 (0)