diff --git a/crates/oxc_cfg/src/block.rs b/crates/oxc_cfg/src/block.rs index 9d94e065c896d..b80a84304f82d 100644 --- a/crates/oxc_cfg/src/block.rs +++ b/crates/oxc_cfg/src/block.rs @@ -47,6 +47,7 @@ impl Instruction { pub enum InstructionKind { Unreachable, Statement, + ImplicitReturn, Return(ReturnInstructionKind), Break(LabeledInstruction), Continue(LabeledInstruction), diff --git a/crates/oxc_cfg/src/builder/mod.rs b/crates/oxc_cfg/src/builder/mod.rs index c325bbe264ea8..201edb4e30960 100644 --- a/crates/oxc_cfg/src/builder/mod.rs +++ b/crates/oxc_cfg/src/builder/mod.rs @@ -111,8 +111,12 @@ impl<'a> ControlFlowGraphBuilder<'a> { self.push_instruction(InstructionKind::Statement, Some(stmt)); } - pub fn push_return(&mut self, kind: ReturnInstructionKind, node: NodeId) { - self.push_instruction(InstructionKind::Return(kind), Some(node)); + pub fn push_return(&mut self, kind: ReturnInstructionKind, node: Option) { + self.push_instruction(InstructionKind::Return(kind), node); + } + + pub fn push_implicit_return(&mut self) { + self.push_instruction(InstructionKind::ImplicitReturn, None); } /// Creates and push a new `BasicBlockId` onto `self.error_path` stack. diff --git a/crates/oxc_cfg/src/dot.rs b/crates/oxc_cfg/src/dot.rs index 9076aab4d85b7..b868716a3df6e 100644 --- a/crates/oxc_cfg/src/dot.rs +++ b/crates/oxc_cfg/src/dot.rs @@ -78,6 +78,7 @@ impl DisplayDot for Instruction { InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) => { "return " } + InstructionKind::ImplicitReturn => "return", InstructionKind::Return(ReturnInstructionKind::NotImplicitUndefined) => { "return " } diff --git a/crates/oxc_linter/src/rules/eslint/getter_return.rs b/crates/oxc_linter/src/rules/eslint/getter_return.rs index 19cfbc69899cd..a3b38c1c740d7 100644 --- a/crates/oxc_linter/src/rules/eslint/getter_return.rs +++ b/crates/oxc_linter/src/rules/eslint/getter_return.rs @@ -257,8 +257,10 @@ impl GetterReturn { match it.kind { // Throws are classified as returning. InstructionKind::Return(_) | InstructionKind::Throw => true, + // Ignore irrelevant elements. - InstructionKind::Break(_) + InstructionKind::ImplicitReturn + | InstructionKind::Break(_) | InstructionKind::Continue(_) | InstructionKind::Iteration(_) | InstructionKind::Unreachable diff --git a/crates/oxc_linter/src/rules/react/require_render_return.rs b/crates/oxc_linter/src/rules/react/require_render_return.rs index 5be0cdf30ed79..aeeda788a1054 100644 --- a/crates/oxc_linter/src/rules/react/require_render_return.rs +++ b/crates/oxc_linter/src/rules/react/require_render_return.rs @@ -127,6 +127,7 @@ fn contains_return_statement(node: &AstNode, ctx: &LintContext) -> bool { return (FoundReturn::No, STOP_WALKING_ON_THIS_PATH); } InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) + | InstructionKind::ImplicitReturn | InstructionKind::Break(_) | InstructionKind::Continue(_) | InstructionKind::Iteration(_) diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 17dc21754f831..72a41990433f4 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -10,7 +10,7 @@ use rustc_hash::FxHashMap; use oxc_ast::{ast::*, AstKind, Visit}; use oxc_cfg::{ - ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind, + ControlFlowGraphBuilder, CtxCursor, CtxFlags, EdgeType, ErrorEdgeKind, InstructionKind, IterationInstructionKind, ReturnInstructionKind, }; use oxc_diagnostics::OxcDiagnostic; @@ -1286,7 +1286,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { /* cfg */ control_flow!(self, |cfg| { - cfg.push_return(ret_kind, node_id); + cfg.push_return(ret_kind, Some(node_id)); cfg.append_unreachable(); }); /* cfg */ @@ -1678,6 +1678,16 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { /* cfg */ control_flow!(self, |cfg| { + let c = cfg.current_basic_block(); + // If the last is an unreachable instruction, it means there is already a explicit + // return or throw statement at the end of function body, we don't need to + // insert an implicit return. + if !matches!( + c.instructions().last().map(|inst| &inst.kind), + Some(InstructionKind::Unreachable) + ) { + cfg.push_implicit_return(); + } cfg.ctx(None).resolve_expect(CtxFlags::FUNCTION); cfg.release_error_harness(error_harness); cfg.pop_finalization_stack(); @@ -1730,6 +1740,16 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { /* cfg */ control_flow!(self, |cfg| { + let c = cfg.current_basic_block(); + // If the last is an unreachable instruction, it means there is already a explicit + // return or throw statement at the end of function body, we don't need to + // insert an implicit return. + if !matches!( + c.instructions().last().map(|inst| &inst.kind), + Some(InstructionKind::Unreachable) + ) { + cfg.push_implicit_return(); + } cfg.ctx(None).resolve_expect(CtxFlags::FUNCTION); cfg.release_error_harness(error_harness); cfg.pop_finalization_stack(); diff --git a/crates/oxc_semantic/src/dot.rs b/crates/oxc_semantic/src/dot.rs index a451901f451f0..9b8eab8284487 100644 --- a/crates/oxc_semantic/src/dot.rs +++ b/crates/oxc_semantic/src/dot.rs @@ -173,6 +173,7 @@ impl DebugDot for Instruction { InstructionKind::Return(ReturnInstructionKind::ImplicitUndefined) => { "return ".to_string() } + InstructionKind::ImplicitReturn => "return".to_string(), InstructionKind::Return(ReturnInstructionKind::NotImplicitUndefined) => { "return ".to_string() } diff --git a/crates/oxc_semantic/tests/integration/snapshots/argument_map.snap b/crates/oxc_semantic/tests/integration/snapshots/argument_map.snap index d7f6ad1f0a610..49a8b0b8e09ed 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/argument_map.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/argument_map.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/argument_map.js +snapshot_kind: text --- bb0: { @@ -17,6 +18,7 @@ bb2: { bb3: { statement + return } bb4: { @@ -28,7 +30,8 @@ digraph { 1 [ label = "bb1" shape = box] 2 [ label = "bb2" shape = box] 3 [ label = "bb3 -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 4 [ label = "bb4" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] diff --git a/crates/oxc_semantic/tests/integration/snapshots/arrow_expressions.snap b/crates/oxc_semantic/tests/integration/snapshots/arrow_expressions.snap index 2f6c0f0485904..642514df66a0b 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/arrow_expressions.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/arrow_expressions.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/arrow_expressions.js +snapshot_kind: text --- bb0: { @@ -18,6 +19,7 @@ bb2: { bb3: { statement + return } bb4: { @@ -26,6 +28,7 @@ bb4: { bb5: { statement + return } digraph { @@ -35,10 +38,12 @@ ExpressionStatement VariableDeclaration" shape = box] 2 [ label = "bb2" shape = box] 3 [ label = "bb3 -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 4 [ label = "bb4" shape = box] 5 [ label = "bb5 -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] 1 -> 3 [ label="NewFunction"] diff --git a/crates/oxc_semantic/tests/integration/snapshots/class.snap b/crates/oxc_semantic/tests/integration/snapshots/class.snap index a814c6bae08b5..fd567f8b448f4 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/class.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/class.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/class.js +snapshot_kind: text --- bb0: { @@ -17,6 +18,7 @@ bb2: { bb3: { statement + return } bb4: { @@ -28,7 +30,8 @@ digraph { 1 [ label = "bb1" shape = box] 2 [ label = "bb2" shape = box] 3 [ label = "bb3 -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 4 [ label = "bb4" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] diff --git a/crates/oxc_semantic/tests/integration/snapshots/cond_expr_in_arrow_fn.snap b/crates/oxc_semantic/tests/integration/snapshots/cond_expr_in_arrow_fn.snap index 6e76a22dc143b..ad9d868900e05 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/cond_expr_in_arrow_fn.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/cond_expr_in_arrow_fn.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/cond_expr_in_arrow_fn.js +snapshot_kind: text --- bb0: { @@ -32,7 +33,7 @@ bb6: { } bb7: { - + return } digraph { @@ -46,7 +47,8 @@ ExpressionStatement" shape = box] Condition(CallExpression(a))" shape = box] 5 [ label = "bb5" shape = box] 6 [ label = "bb6" shape = box] - 7 [ label = "bb7" shape = box] + 7 [ label = "bb7 +return" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] 1 -> 3 [ label="NewFunction"] diff --git a/crates/oxc_semantic/tests/integration/snapshots/do_while_break.snap b/crates/oxc_semantic/tests/integration/snapshots/do_while_break.snap index 0e28ae6a6c2f1..3edb6557450d1 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/do_while_break.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/do_while_break.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/do_while_break.js +snapshot_kind: text --- bb0: { @@ -50,7 +51,7 @@ bb10: { } bb11: { - + return } bb12: { @@ -78,7 +79,8 @@ unreachable" shape = box] 9 [ label = "bb9 Condition(true)" shape = box] 10 [ label = "bb10" shape = box] - 11 [ label = "bb11" shape = box] + 11 [ label = "bb11 +return" shape = box] 12 [ label = "bb12" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] diff --git a/crates/oxc_semantic/tests/integration/snapshots/for_in.snap b/crates/oxc_semantic/tests/integration/snapshots/for_in.snap index 8deef695b0b79..a589a36942520 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/for_in.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/for_in.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/for_in.js +snapshot_kind: text --- bb0: { @@ -34,6 +35,7 @@ bb6: { bb7: { statement + return } bb8: { @@ -53,7 +55,8 @@ Condition(test)" shape = box] 6 [ label = "bb6 ExpressionStatement" shape = box] 7 [ label = "bb7 -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 8 [ label = "bb8" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] diff --git a/crates/oxc_semantic/tests/integration/snapshots/function_as_expression.snap b/crates/oxc_semantic/tests/integration/snapshots/function_as_expression.snap index fd18d78cfd187..840cc2cd26ce9 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/function_as_expression.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/function_as_expression.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/function_as_expression.js +snapshot_kind: text --- bb0: { @@ -16,7 +17,7 @@ bb2: { } bb3: { - + return } bb4: { @@ -28,7 +29,7 @@ bb5: { } bb6: { - + return } bb7: { @@ -40,7 +41,7 @@ bb8: { } bb9: { - + return } bb10: { @@ -52,7 +53,7 @@ bb11: { } bb12: { - + return } bb13: { @@ -64,7 +65,7 @@ bb14: { } bb15: { - + return } bb16: { @@ -76,7 +77,7 @@ bb17: { } bb18: { - + return } bb19: { @@ -88,7 +89,7 @@ bb20: { } bb21: { - + return } bb22: { @@ -100,7 +101,7 @@ bb23: { } bb24: { - + return } bb25: { @@ -120,7 +121,7 @@ bb28: { } bb29: { - + return } bb30: { @@ -140,7 +141,7 @@ bb33: { } bb34: { - + return } bb35: { @@ -152,7 +153,7 @@ bb36: { } bb37: { - + return } bb38: { @@ -164,50 +165,61 @@ digraph { 1 [ label = "bb1 ExpressionStatement" shape = box] 2 [ label = "bb2" shape = box] - 3 [ label = "bb3" shape = box] + 3 [ label = "bb3 +return" shape = box] 4 [ label = "bb4 ExpressionStatement" shape = box] 5 [ label = "bb5" shape = box] - 6 [ label = "bb6" shape = box] + 6 [ label = "bb6 +return" shape = box] 7 [ label = "bb7" shape = box] 8 [ label = "bb8" shape = box] - 9 [ label = "bb9" shape = box] + 9 [ label = "bb9 +return" shape = box] 10 [ label = "bb10 ExpressionStatement" shape = box] 11 [ label = "bb11" shape = box] - 12 [ label = "bb12" shape = box] + 12 [ label = "bb12 +return" shape = box] 13 [ label = "bb13 ExpressionStatement" shape = box] 14 [ label = "bb14" shape = box] - 15 [ label = "bb15" shape = box] + 15 [ label = "bb15 +return" shape = box] 16 [ label = "bb16 ExpressionStatement" shape = box] 17 [ label = "bb17" shape = box] - 18 [ label = "bb18" shape = box] + 18 [ label = "bb18 +return" shape = box] 19 [ label = "bb19 ExpressionStatement" shape = box] 20 [ label = "bb20" shape = box] - 21 [ label = "bb21" shape = box] + 21 [ label = "bb21 +return" shape = box] 22 [ label = "bb22 ExpressionStatement" shape = box] 23 [ label = "bb23" shape = box] - 24 [ label = "bb24" shape = box] + 24 [ label = "bb24 +return" shape = box] 25 [ label = "bb25" shape = box] 26 [ label = "bb26" shape = box] 27 [ label = "bb27 ExpressionStatement" shape = box] 28 [ label = "bb28" shape = box] - 29 [ label = "bb29" shape = box] + 29 [ label = "bb29 +return" shape = box] 30 [ label = "bb30" shape = box] 31 [ label = "bb31" shape = box] 32 [ label = "bb32 ExpressionStatement" shape = box] 33 [ label = "bb33" shape = box] - 34 [ label = "bb34" shape = box] + 34 [ label = "bb34 +return" shape = box] 35 [ label = "bb35 ExpressionStatement" shape = box] 36 [ label = "bb36" shape = box] - 37 [ label = "bb37" shape = box] + 37 [ label = "bb37 +return" shape = box] 38 [ label = "bb38" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] diff --git a/crates/oxc_semantic/tests/integration/snapshots/function_in_finally.snap b/crates/oxc_semantic/tests/integration/snapshots/function_in_finally.snap index 6f0df7a79df54..ecec5a3ebdf26 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/function_in_finally.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/function_in_finally.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/function_in_finally.js +snapshot_kind: text --- bb0: { @@ -29,7 +30,7 @@ bb5: { } bb6: { - + return } bb7: { @@ -51,7 +52,8 @@ ExpressionStatement" shape = box] 4 [ label = "bb4 BlockStatement" shape = box] 5 [ label = "bb5" shape = box] - 6 [ label = "bb6" shape = box] + 6 [ label = "bb6 +return" shape = box] 7 [ label = "bb7 ExpressionStatement" shape = box] 8 [ label = "bb8" shape = box] diff --git a/crates/oxc_semantic/tests/integration/snapshots/if_stmt_in_for_in.snap b/crates/oxc_semantic/tests/integration/snapshots/if_stmt_in_for_in.snap index cc9f28f5d4ad3..4fd8b96effc79 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/if_stmt_in_for_in.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/if_stmt_in_for_in.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/if_stmt_in_for_in.js +snapshot_kind: text --- bb0: { @@ -76,6 +77,7 @@ bb15: { bb16: { statement + return } bb17: { @@ -118,7 +120,8 @@ unreachable" shape = box] ExpressionStatement ExpressionStatement" shape = box] 16 [ label = "bb16 -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 17 [ label = "bb17" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] diff --git a/crates/oxc_semantic/tests/integration/snapshots/import_as_function.snap b/crates/oxc_semantic/tests/integration/snapshots/import_as_function.snap index f6f89ad26263d..3a59130ed0363 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/import_as_function.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/import_as_function.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/import_as_function.js +snapshot_kind: text --- bb0: { @@ -17,6 +18,7 @@ bb2: { bb3: { statement + return } digraph { @@ -25,7 +27,8 @@ digraph { ExpressionStatement" shape = box] 2 [ label = "bb2" shape = box] 3 [ label = "bb3 -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] 1 -> 3 [ label="NewFunction"] diff --git a/crates/oxc_semantic/tests/integration/snapshots/parenthesized_and_sequence_expr.snap b/crates/oxc_semantic/tests/integration/snapshots/parenthesized_and_sequence_expr.snap index 98c8e699b5938..b8a13b0ee3a60 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/parenthesized_and_sequence_expr.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/parenthesized_and_sequence_expr.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/parenthesized_and_sequence_expr.js +snapshot_kind: text --- bb0: { @@ -17,6 +18,7 @@ bb2: { bb3: { statement + return } bb4: { @@ -29,6 +31,7 @@ bb5: { bb6: { statement + return } bb7: { @@ -40,11 +43,13 @@ digraph { 1 [ label = "bb1" shape = box] 2 [ label = "bb2" shape = box] 3 [ label = "bb3 -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 4 [ label = "bb4" shape = box] 5 [ label = "bb5" shape = box] 6 [ label = "bb6 -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 7 [ label = "bb7" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red] diff --git a/crates/oxc_semantic/tests/integration/snapshots/yield_as_statement_and_expression.snap b/crates/oxc_semantic/tests/integration/snapshots/yield_as_statement_and_expression.snap index 09a16b6ec12c7..31b39ec8f2123 100644 --- a/crates/oxc_semantic/tests/integration/snapshots/yield_as_statement_and_expression.snap +++ b/crates/oxc_semantic/tests/integration/snapshots/yield_as_statement_and_expression.snap @@ -2,6 +2,7 @@ source: crates/oxc_semantic/tests/integration/cfg.rs expression: snapshot input_file: crates/oxc_semantic/tests/integration/cfg_fixtures/yield_as_statement_and_expression.js +snapshot_kind: text --- bb0: { @@ -18,6 +19,7 @@ bb2: { bb3: { statement statement + return } bb4: { @@ -31,7 +33,8 @@ VariableDeclaration" shape = box] 2 [ label = "bb2" shape = box] 3 [ label = "bb3 ExpressionStatement -ExpressionStatement" shape = box] +ExpressionStatement +return" shape = box] 4 [ label = "bb4" shape = box] 1 -> 0 [ label="Error(Implicit)", style=dashed, color=red] 3 -> 2 [ label="Error(Implicit)", style=dashed, color=red]