diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index b8fcc8149..4ff910f98 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -666,6 +666,73 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { self.leave_node(kind); } + fn visit_logical_expression(&mut self, expr: &LogicalExpression<'a>) { + // logical expressions are short-circuiting, and therefore + // also represent control flow. + // For example, in: + // foo && bar(); + // the bar() call will only be executed if foo is truthy. + let kind = AstKind::LogicalExpression(self.alloc(expr)); + self.enter_node(kind); + + self.visit_expression(&expr.left); + + /* cfg */ + let left_expr_end_ix = self.cfg.current_node_ix; + let right_expr_start_ix = self.cfg.new_basic_block(); + /* cfg */ + + self.visit_expression(&expr.right); + + /* cfg */ + let right_expr_end_ix = self.cfg.current_node_ix; + let after_logical_expr_ix = self.cfg.new_basic_block(); + + self.cfg.add_edge(left_expr_end_ix, right_expr_start_ix, EdgeType::Normal); + self.cfg.add_edge(left_expr_end_ix, after_logical_expr_ix, EdgeType::Normal); + self.cfg.add_edge(right_expr_end_ix, after_logical_expr_ix, EdgeType::Normal); + /* cfg */ + + self.leave_node(kind); + } + + fn visit_assignment_expression(&mut self, expr: &AssignmentExpression<'a>) { + // assignment expressions can include an operator, which + // can be used to determine the control flow of the expression. + // For example, in: + // foo &&= super(); + // the super() call will only be executed if foo is truthy. + + let kind = AstKind::AssignmentExpression(self.alloc(expr)); + self.enter_node(kind); + self.visit_assignment_target(&expr.left); + + /* cfg */ + let cfg_ixs = if expr.operator.is_logical() { + let target_end_ix = self.cfg.current_node_ix; + let expr_start_ix = self.cfg.new_basic_block(); + Some((target_end_ix, expr_start_ix)) + } else { + None + }; + /* cfg */ + + self.visit_expression(&expr.right); + + /* cfg */ + if let Some((target_end_ix, expr_start_ix)) = cfg_ixs { + let expr_end_ix = self.cfg.current_node_ix; + let after_assignment_ix = self.cfg.new_basic_block(); + + self.cfg.add_edge(target_end_ix, expr_start_ix, EdgeType::Normal); + self.cfg.add_edge(target_end_ix, after_assignment_ix, EdgeType::Normal); + self.cfg.add_edge(expr_end_ix, after_assignment_ix, EdgeType::Normal); + } + /* cfg */ + + self.leave_node(kind); + } + fn visit_for_statement(&mut self, stmt: &ForStatement<'a>) { let kind = AstKind::ForStatement(self.alloc(stmt)); let is_lexical_declaration = diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@assignment_operators.js-2.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@assignment_operators.js-2.snap index 9589a1e0a..a8386b028 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@assignment_operators.js-2.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@assignment_operators.js-2.snap @@ -5,5 +5,20 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/assignment_operators.js --- digraph { 0 [ label = ""] + 1 [ label = ""] + 2 [ label = ""] + 3 [ label = ""] + 4 [ label = ""] + 5 [ label = ""] + 6 [ label = ""] + 0 -> 1 [ ] + 0 -> 2 [ ] + 1 -> 2 [ ] + 2 -> 3 [ ] + 2 -> 4 [ ] + 3 -> 4 [ ] + 4 -> 5 [ ] + 4 -> 6 [ ] + 5 -> 6 [ ] } diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@assignment_operators.js.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@assignment_operators.js.snap index b15d9d768..506272bc5 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@assignment_operators.js.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@assignment_operators.js.snap @@ -6,3 +6,27 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/assignment_operators.js bb0: { } + +bb1: { + +} + +bb2: { + +} + +bb3: { + +} + +bb4: { + +} + +bb5: { + +} + +bb6: { + +} diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@function_as_expression.js-2.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@function_as_expression.js-2.snap index 5a025b513..78c918124 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@function_as_expression.js-2.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@function_as_expression.js-2.snap @@ -27,6 +27,10 @@ digraph { 20 [ label = ""] 21 [ label = ""] 22 [ label = ""] + 23 [ label = ""] + 24 [ label = ""] + 25 [ label = ""] + 26 [ label = ""] 0 -> 1 [ ] 0 -> 2 [ ] 2 -> 3 [ ] @@ -45,9 +49,15 @@ digraph { 14 -> 16 [ ] 16 -> 17 [ ] 16 -> 18 [ ] + 17 -> 18 [ ] 18 -> 19 [ ] 18 -> 20 [ ] 20 -> 21 [ ] 20 -> 22 [ ] + 21 -> 22 [ ] + 22 -> 23 [ ] + 22 -> 24 [ ] + 24 -> 25 [ ] + 24 -> 26 [ ] } diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@function_as_expression.js.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@function_as_expression.js.snap index bb117691e..b6e78092c 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@function_as_expression.js.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@function_as_expression.js.snap @@ -94,3 +94,19 @@ bb21: { bb22: { } + +bb23: { + +} + +bb24: { + +} + +bb25: { + +} + +bb26: { + +} diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@if_else.js-2.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@if_else.js-2.snap index bd56990c1..60356b80c 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@if_else.js-2.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@if_else.js-2.snap @@ -6,26 +6,31 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/if_else.js digraph { 0 [ label = ""] 1 [ label = ""] - 2 [ label = "$return = "] + 2 [ label = ""] 3 [ label = ""] - 4 [ label = "Unreachable()"] - 5 [ label = "$return = "] - 6 [ label = ""] - 7 [ label = "Unreachable()"] + 4 [ label = "$return = "] + 5 [ label = ""] + 6 [ label = "Unreachable()"] + 7 [ label = "$return = "] 8 [ label = ""] - 9 [ label = "Unreachable()\n$return = "] + 9 [ label = "Unreachable()"] 10 [ label = ""] - 11 [ label = "Unreachable()"] + 11 [ label = "Unreachable()\n$return = "] 12 [ label = ""] + 13 [ label = "Unreachable()"] + 14 [ label = ""] 0 -> 1 [ ] - 3 -> 4 [ ] - 6 -> 7 [ ] - 8 -> 9 [ ] - 4 -> 8 [ ] 1 -> 2 [ ] - 1 -> 5 [ ] - 7 -> 8 [ ] + 1 -> 3 [ ] + 2 -> 3 [ ] + 5 -> 6 [ ] + 8 -> 9 [ ] 10 -> 11 [ ] - 0 -> 12 [ ] + 6 -> 10 [ ] + 3 -> 4 [ ] + 3 -> 7 [ ] + 9 -> 10 [ ] + 12 -> 13 [ ] + 0 -> 14 [ ] } diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@if_else.js.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@if_else.js.snap index 67df1a869..71b1c3394 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@if_else.js.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@if_else.js.snap @@ -12,7 +12,7 @@ bb1: { } bb2: { - $return = + } bb3: { @@ -20,28 +20,27 @@ bb3: { } bb4: { - Unreachable() -} - -bb5: { $return = } -bb6: { +bb5: { } -bb7: { +bb6: { Unreachable() } +bb7: { + $return = +} + bb8: { } bb9: { Unreachable() - $return = } bb10: { @@ -50,8 +49,17 @@ bb10: { bb11: { Unreachable() + $return = } bb12: { } + +bb13: { + Unreachable() +} + +bb14: { + +} diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@infix_operators.js-2.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@infix_operators.js-2.snap index 75f875e52..bdc2e2fd4 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@infix_operators.js-2.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@infix_operators.js-2.snap @@ -5,5 +5,10 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/infix_operators.js --- digraph { 0 [ label = ""] + 1 [ label = ""] + 2 [ label = ""] + 0 -> 1 [ ] + 0 -> 2 [ ] + 1 -> 2 [ ] } diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@infix_operators.js.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@infix_operators.js.snap index abb07f317..4186448c1 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@infix_operators.js.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@infix_operators.js.snap @@ -6,3 +6,11 @@ input_file: crates/oxc_semantic/tests/cfg_fixtures/infix_operators.js bb0: { } + +bb1: { + +} + +bb2: { + +} diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@logical_expressions_short_circuit.js-2.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@logical_expressions_short_circuit.js-2.snap index da9d67b63..9379386d5 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@logical_expressions_short_circuit.js-2.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@logical_expressions_short_circuit.js-2.snap @@ -11,14 +11,29 @@ digraph { 4 [ label = ""] 5 [ label = ""] 6 [ label = ""] - 1 -> 2 [ ] + 7 [ label = ""] + 8 [ label = ""] + 9 [ label = ""] + 10 [ label = ""] + 11 [ label = ""] + 12 [ label = ""] 0 -> 1 [ ] 0 -> 2 [ ] + 1 -> 2 [ ] 3 -> 4 [ ] 2 -> 3 [ ] 2 -> 4 [ ] - 5 -> 6 [ ] 4 -> 5 [ ] 4 -> 6 [ ] + 5 -> 6 [ ] + 7 -> 8 [ ] + 6 -> 7 [ ] + 6 -> 8 [ ] + 8 -> 9 [ ] + 8 -> 10 [ ] + 9 -> 10 [ ] + 11 -> 12 [ ] + 10 -> 11 [ ] + 10 -> 12 [ ] } diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@logical_expressions_short_circuit.js.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@logical_expressions_short_circuit.js.snap index b71107bb4..6ff62739b 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@logical_expressions_short_circuit.js.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@logical_expressions_short_circuit.js.snap @@ -30,3 +30,27 @@ bb5: { bb6: { } + +bb7: { + +} + +bb8: { + +} + +bb9: { + +} + +bb10: { + +} + +bb11: { + +} + +bb12: { + +} diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@while.js-2.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@while.js-2.snap index 958fdb16e..cf4148955 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@while.js-2.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@while.js-2.snap @@ -7,21 +7,26 @@ digraph { 0 [ label = ""] 1 [ label = ""] 2 [ label = ""] - 3 [ label = "$return = "] + 3 [ label = ""] 4 [ label = ""] - 5 [ label = "Unreachable()"] - 6 [ label = "$return = "] - 7 [ label = ""] - 8 [ label = "Unreachable()"] + 5 [ label = "$return = "] + 6 [ label = ""] + 7 [ label = "Unreachable()"] + 8 [ label = "$return = "] 9 [ label = ""] + 10 [ label = "Unreachable()"] + 11 [ label = ""] 0 -> 1 [ ] - 4 -> 5 [ ] - 1 -> 2 [ ] 2 -> 3 [ ] - 3 -> 6 [ ] - 3 -> 2 [ ] - 2 -> 6 [ ] - 7 -> 8 [ ] - 0 -> 9 [ ] + 2 -> 4 [ ] + 3 -> 4 [ ] + 6 -> 7 [ ] + 1 -> 2 [ ] + 2 -> 5 [ ] + 5 -> 8 [ ] + 5 -> 2 [ ] + 2 -> 8 [ ] + 9 -> 10 [ ] + 0 -> 11 [ ] } diff --git a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@while.js.snap b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@while.js.snap index 496c594f5..6b7496791 100644 --- a/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@while.js.snap +++ b/crates/oxc_semantic/tests/snapshots/cfg__cfg_files@while.js.snap @@ -16,7 +16,7 @@ bb2: { } bb3: { - $return = + } bb4: { @@ -24,21 +24,29 @@ bb4: { } bb5: { - Unreachable() -} - -bb6: { $return = } -bb7: { +bb6: { } -bb8: { +bb7: { Unreachable() } +bb8: { + $return = +} + bb9: { } + +bb10: { + Unreachable() +} + +bb11: { + +}