Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/oxc_linter/src/rules/eslint/getter_return.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ impl GetterReturn {
| InstructionKind::Break(_)
| InstructionKind::Continue(_)
| InstructionKind::Iteration(_)
| InstructionKind::Condition
| InstructionKind::Statement => {}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ fn contains_return_statement<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> b
| InstructionKind::Break(_)
| InstructionKind::Continue(_)
| InstructionKind::Iteration(_)
| InstructionKind::Condition
| InstructionKind::Statement => {}
}
}
Expand Down
61 changes: 45 additions & 16 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,10 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let start_of_condition_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.record_ast_nodes();
self.visit_expression(&stmt.test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(start_of_condition_graph_ix, test_node);

/* cfg */
let end_of_condition_graph_ix = self.cfg.current_node_ix;
Expand Down Expand Up @@ -675,10 +678,18 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let kind = AstKind::ConditionalExpression(self.alloc(expr));
self.enter_node(kind);

/* cfg - condition basic block */
let before_conditional_graph_ix = self.cfg.current_node_ix;
let start_of_condition_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.record_ast_nodes();
self.visit_expression(&expr.test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(start_of_condition_graph_ix, test_node);

/* cfg */
let before_conditional_expr_graph_ix = self.cfg.current_node_ix;
let after_condition_graph_ix = self.cfg.current_node_ix;
// conditional expression basic block
let before_consequent_expr_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */
Expand All @@ -696,25 +707,28 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let after_alternate_graph_ix = self.cfg.current_node_ix;
/* bb after conditional expression joins consequent and alternate */
let after_conditional_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.cfg.add_edge(
before_conditional_graph_ix,
start_of_condition_graph_ix,
EdgeType::Normal,
);

self.cfg.add_edge(
after_consequent_expr_graph_ix,
after_conditional_graph_ix,
EdgeType::Normal,
);
self.cfg.add_edge(
before_conditional_expr_graph_ix,
after_condition_graph_ix,
before_consequent_expr_graph_ix,
EdgeType::Normal,
EdgeType::Jump,
);

self.cfg.add_edge(
before_conditional_expr_graph_ix,
start_alternate_graph_ix,
EdgeType::Normal,
);
self.cfg.add_edge(after_condition_graph_ix, start_alternate_graph_ix, EdgeType::Normal);
self.cfg.add_edge(after_alternate_graph_ix, after_conditional_graph_ix, EdgeType::Normal);
/* cfg */

self.leave_node(kind);
}

Expand All @@ -735,7 +749,10 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let test_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */
if let Some(test) = &stmt.test {
self.record_ast_nodes();
self.visit_expression(test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(test_graph_ix, test_node);
}

/* cfg */
Expand Down Expand Up @@ -916,10 +933,18 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let kind = AstKind::IfStatement(self.alloc(stmt));
self.enter_node(kind);

/* cfg - condition basic block */
let before_if_stmt_graph_ix = self.cfg.current_node_ix;
let start_of_condition_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.record_ast_nodes();
self.visit_expression(&stmt.test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(start_of_condition_graph_ix, test_node);

/* cfg */
let before_if_stmt_graph_ix = self.cfg.current_node_ix;
let after_test_graph_ix = self.cfg.current_node_ix;
let before_consequent_stmt_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

Expand All @@ -944,13 +969,11 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
/* cfg - bb after if statement joins consequent and alternate */
let after_if_graph_ix = self.cfg.new_basic_block_normal();

self.cfg.add_edge(before_if_stmt_graph_ix, start_of_condition_graph_ix, EdgeType::Normal);

self.cfg.add_edge(after_consequent_stmt_graph_ix, after_if_graph_ix, EdgeType::Normal);

self.cfg.add_edge(
before_if_stmt_graph_ix,
before_consequent_stmt_graph_ix,
EdgeType::Normal,
);
self.cfg.add_edge(after_test_graph_ix, before_consequent_stmt_graph_ix, EdgeType::Jump);

if let Some((start_of_alternate_stmt_graph_ix, after_alternate_stmt_graph_ix)) =
else_graph_ix
Expand Down Expand Up @@ -1105,13 +1128,16 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
self.enter_node(kind);

if let Some(expr) = &case.test {
self.record_ast_nodes();
self.visit_expression(expr);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(self.cfg.current_node_ix, test_node);
}

/* cfg */
let after_test_graph_ix = self.cfg.current_node_ix;
let statements_in_switch_graph_ix = self.cfg.new_basic_block_normal();
self.cfg.add_edge(after_test_graph_ix, statements_in_switch_graph_ix, EdgeType::Normal);
self.cfg.add_edge(after_test_graph_ix, statements_in_switch_graph_ix, EdgeType::Jump);
/* cfg */

self.visit_statements(&case.consequent);
Expand Down Expand Up @@ -1274,7 +1300,10 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
let condition_graph_ix = self.cfg.new_basic_block_normal();
/* cfg */

self.record_ast_nodes();
self.visit_expression(&stmt.test);
let test_node = self.retrieve_recorded_ast_nodes().into_iter().next();
self.cfg.append_condition_to(condition_graph_ix, test_node);

/* cfg - body basic block */
let body_graph_ix = self.cfg.new_basic_block_normal();
Expand Down
24 changes: 21 additions & 3 deletions crates/oxc_semantic/src/control_flow/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,15 @@ impl<'a> ControlFlowGraphBuilder<'a> {
ControlFlowGraph { graph: self.graph, basic_blocks: self.basic_blocks }
}

/// # Panics
pub fn current_basic_block(&mut self) -> &mut BasicBlock {
self.basic_block_mut(self.current_node_ix)
}

/// # Panics
pub fn basic_block_mut(&mut self, basic_block: BasicBlockId) -> &mut BasicBlock {
let idx = *self
.graph
.node_weight(self.current_node_ix)
.node_weight(basic_block)
.expect("expected `self.current_node_ix` to be a valid node index in self.graph");
self.basic_blocks
.get_mut(idx)
Expand Down Expand Up @@ -145,6 +149,10 @@ impl<'a> ControlFlowGraphBuilder<'a> {
);
}

pub fn append_condition_to(&mut self, block: BasicBlockId, node: Option<AstNodeId>) {
self.push_instruction_to(block, InstructionKind::Condition, node);
}

pub fn append_iteration(&mut self, node: Option<AstNodeId>, kind: IterationInstructionKind) {
self.push_instruction(InstructionKind::Iteration(kind), node);
}
Expand Down Expand Up @@ -195,7 +203,17 @@ impl<'a> ControlFlowGraphBuilder<'a> {

#[inline]
pub(self) fn push_instruction(&mut self, kind: InstructionKind, node_id: Option<AstNodeId>) {
self.current_basic_block().instructions.push(Instruction { kind, node_id });
self.push_instruction_to(self.current_node_ix, kind, node_id);
}

#[inline]
pub(self) fn push_instruction_to(
&mut self,
block: BasicBlockId,
kind: InstructionKind,
node_id: Option<AstNodeId>,
) {
self.basic_block_mut(block).instructions.push(Instruction { kind, node_id });
}

#[must_use]
Expand Down
22 changes: 20 additions & 2 deletions crates/oxc_semantic/src/control_flow/dot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ impl<'a, 'b> DebugDotContext<'a, 'b> {
fn debug_ast_kind(self, id: AstNodeId) -> String {
self.0.kind(id).debug_name().into_owned()
}

fn try_eval_literal(self, id: AstNodeId) -> Option<String> {
match self.0.kind(id) {
AstKind::NumericLiteral(lit) => Some(lit.value.to_string()),
AstKind::BooleanLiteral(lit) => Some(lit.value.to_string()),
AstKind::StringLiteral(lit) => Some(lit.value.to_string()),
AstKind::BigintLiteral(lit) => Some(lit.raw.to_string()),
AstKind::NullLiteral(_) => Some("null".to_string()),
_ => None,
}
}
}

impl<'a, 'b> From<&'b AstNodes<'a>> for DebugDotContext<'a, 'b> {
Expand Down Expand Up @@ -76,12 +87,13 @@ impl DisplayDot for Instruction {
InstructionKind::Statement => "statement",
InstructionKind::Unreachable => "unreachable",
InstructionKind::Throw => "throw",
InstructionKind::Condition => "condition",
InstructionKind::Iteration(IterationInstructionKind::Of) => "iteration <of>",
InstructionKind::Iteration(IterationInstructionKind::In) => "iteration <in>",
InstructionKind::Break(LabeledInstruction::Labeled) => "break <label>",
InstructionKind::Break(LabeledInstruction::Unlabeled) => "break",
InstructionKind::Continue(LabeledInstruction::Labeled) => "continue <label>",
InstructionKind::Continue(LabeledInstruction::Unlabeled) => "continue",
InstructionKind::Iteration(IterationInstructionKind::Of) => "iteration <of>",
InstructionKind::Iteration(IterationInstructionKind::In) => "iteration <in>",
InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) => {
"return <implicit undefined>"
}
Expand Down Expand Up @@ -136,6 +148,12 @@ impl DebugDot for Instruction {
}
InstructionKind::Unreachable => "unreachable".to_string(),
InstructionKind::Throw => "throw".to_string(),
InstructionKind::Condition => self.node_id.map_or("None".to_string(), |id| {
format!(
"Condition({})",
ctx.try_eval_literal(id).unwrap_or_else(|| ctx.debug_ast_kind(id))
)
}),
InstructionKind::Iteration(ref kind) => {
format!(
"Iteration({} {} {})",
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_semantic/src/control_flow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ pub enum InstructionKind {
Break(LabeledInstruction),
Continue(LabeledInstruction),
Throw,
Condition,
Iteration(IterationInstructionKind),
}
#[derive(Debug, Clone)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ digraph {
1 [ label = "VariableDeclaration" ]
2 [ label = "" ]
3 [ label = "ExpressionStatement" ]
4 [ label = "" ]
4 [ label = "Condition(CallExpression)" ]
5 [ label = "" ]
6 [ label = "" ]
7 [ label = "" ]
1 -> 0 [ label = "Error(Implicit)" ]
3 -> 2 [ label = "Error(Implicit)" ]
1 -> 3 [ label = "NewFunction" ]
4 -> 2 [ label = "Error(Implicit)" ]
5 -> 2 [ label = "Error(Implicit)" ]
6 -> 2 [ label = "Error(Implicit)" ]
4 -> 6 [ label = "Normal" ]
7 -> 2 [ label = "Error(Implicit)" ]
3 -> 4 [ label = "Normal" ]
3 -> 5 [ label = "Normal" ]
5 -> 6 [ label = "Normal" ]
5 -> 7 [ label = "Normal" ]
4 -> 5 [ label = "Jump" ]
4 -> 6 [ label = "Normal" ]
6 -> 7 [ label = "Normal" ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ bb3: {
}

bb4: {

condition
}

bb5: {
Expand All @@ -30,3 +30,7 @@ bb5: {
bb6: {

}

bb7: {

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/conditional_expre
digraph {
0 [ label = "" ]
1 [ label = "VariableDeclaration" ]
2 [ label = "" ]
2 [ label = "Condition(CallExpression)" ]
3 [ label = "" ]
4 [ label = "VariableDeclaration" ]
4 [ label = "" ]
5 [ label = "VariableDeclaration" ]
1 -> 0 [ label = "Error(Implicit)" ]
2 -> 0 [ label = "Error(Implicit)" ]
3 -> 0 [ label = "Error(Implicit)" ]
4 -> 0 [ label = "Error(Implicit)" ]
2 -> 4 [ label = "Normal" ]
5 -> 0 [ label = "Error(Implicit)" ]
1 -> 2 [ label = "Normal" ]
1 -> 3 [ label = "Normal" ]
3 -> 4 [ label = "Normal" ]
3 -> 5 [ label = "Normal" ]
2 -> 3 [ label = "Jump" ]
2 -> 4 [ label = "Normal" ]
4 -> 5 [ label = "Normal" ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ bb1: {
}

bb2: {

condition
}

bb3: {

}

bb4: {

}

bb5: {
statement
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ digraph {
6 [ label = "DoWhileStatement" ]
7 [ label = "BlockStatement\nbreak" ]
8 [ label = "unreachable" ]
9 [ label = "" ]
9 [ label = "Condition(true)" ]
10 [ label = "" ]
11 [ label = "" ]
12 [ label = "" ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ bb8: {
}

bb9: {

condition
}

bb10: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ digraph {
1 [ label = "" ]
2 [ label = "" ]
3 [ label = "ForStatement\nVariableDeclaration" ]
4 [ label = "" ]
4 [ label = "Condition(test)" ]
5 [ label = "" ]
6 [ label = "ExpressionStatement" ]
7 [ label = "ExpressionStatement" ]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ bb3: {
}

bb4: {

condition
}

bb5: {
Expand Down
Loading