Repository: swc-project/swc (rust)

Issue:
minify switches in 1.2.171 does not remove a break statement inside a block
### Describe the bug

The `switches` [PR](https://github.com/swc-project/swc/pull/4260) has a bug since it does not remove a `break` statement that's inside a block. See minimal repro code below.

### Input code

```typescript
export function foo(arg) {
	switch (arg) {
		case ENUM_VALUE: {
			const {data} = arg
			call(data)
			break
		}
		default:
			break
	}
}
```


### Config

```json
{
  "sourceMaps": true,
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "dynamicImport": true,
      "decorators": true
    },
    "preserveAllComments": true,
    "transform": null,
    "target": "es2017",
    "loose": false,
    "externalHelpers": false,
    "keepClassNames": false,
    "minify": { "compress": true }
  }
}
```


### Playground link

_No response_

### Expected behavior

The following output is generated:

```ts
export function foo(arg) {
    if (arg === ENUM_VALUE) {
        let { data  } = arg;
        call(data);
        break; // <-- remove this break
    }
}
```

Notice the break statement that causes a syntax error.

### Actual behavior

_No response_

### Version

1.2.171

### Additional context

_No response_

Pull request description:
fix(es/minifier): remove last break in last `BlockStmt`
<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**

<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
closes #4412 

Patch:
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/switches.rs b/crates/swc_ecma_minifier/src/compress/optimize/switches.rs
index 9774c44562..d3ba7daf26 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/switches.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/switches.rs
@@ -439,12 +439,14 @@ where
 }
 
 fn remove_last_break(stmt: &mut Vec<Stmt>) -> bool {
-    if let Some(Stmt::Break(BreakStmt { label: None, .. })) = stmt.last() {
-        report_change!("switches: Removing `break` at the end");
-        stmt.pop();
-        true
-    } else {
-        false
+    match stmt.last_mut() {
+        Some(Stmt::Break(BreakStmt { label: None, .. })) => {
+            report_change!("switches: Removing `break` at the end");
+            stmt.pop();
+            true
+        }
+        Some(Stmt::Block(BlockStmt { stmts, .. })) => remove_last_break(stmts),
+        _ => false,
     }
 }
 


Test patch:
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/4412/analysis-snapshot.rust-debug b/crates/swc_ecma_minifier/tests/fixture/issues/4412/analysis-snapshot.rust-debug
new file mode 100644
index 0000000000..0e7002b067
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/4412/analysis-snapshot.rust-debug
@@ -0,0 +1,196 @@
+TestSnapshot {
+    vars: [
+        (
+            (
+                Atom('ENUM_VALUE' type=dynamic),
+                #1,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 1,
+                cond_init: false,
+                declared: false,
+                declared_count: 0,
+                declared_as_fn_param: false,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 0,
+                usage_count: 1,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: false,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: true,
+                is_fn_local: true,
+                used_by_nested_fn: true,
+                executed_multiple_time: false,
+                used_in_cond: false,
+                var_kind: None,
+                var_initialized: false,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: false,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+        (
+            (
+                Atom('arg' type=inline),
+                #2,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 2,
+                cond_init: false,
+                declared: true,
+                declared_count: 1,
+                declared_as_fn_param: true,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 0,
+                usage_count: 2,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: false,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: false,
+                is_fn_local: true,
+                used_by_nested_fn: true,
+                executed_multiple_time: false,
+                used_in_cond: true,
+                var_kind: None,
+                var_initialized: false,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: false,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+        (
+            (
+                Atom('call' type=static),
+                #1,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 1,
+                cond_init: false,
+                declared: false,
+                declared_count: 0,
+                declared_as_fn_param: false,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 0,
+                usage_count: 1,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: false,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: true,
+                is_fn_local: true,
+                used_by_nested_fn: true,
+                executed_multiple_time: false,
+                used_in_cond: true,
+                var_kind: None,
+                var_initialized: false,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: true,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+        (
+            (
+                Atom('data' type=inline),
+                #3,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 1,
+                cond_init: true,
+                declared: true,
+                declared_count: 1,
+                declared_as_fn_param: false,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 1,
+                usage_count: 1,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: true,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: false,
+                is_fn_local: true,
+                used_by_nested_fn: true,
+                executed_multiple_time: false,
+                used_in_cond: true,
+                var_kind: Some(
+                    "const",
+                ),
+                var_initialized: true,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: false,
+                used_as_arg: true,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+        (
+            (
+                Atom('foo' type=inline),
+                #1,
+            ),
+            VarUsageInfo {
+                inline_prevented: true,
+                ref_count: 0,
+                cond_init: false,
+                declared: true,
+                declared_count: 1,
+                declared_as_fn_param: false,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 0,
+                usage_count: 0,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: false,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: false,
+                is_fn_local: true,
+                used_by_nested_fn: false,
+                executed_multiple_time: false,
+                used_in_cond: false,
+                var_kind: None,
+                var_initialized: true,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: false,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+    ],
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/4412/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/4412/input.js
new file mode 100644
index 0000000000..284e8d486e
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/4412/input.js
@@ -0,0 +1,11 @@
+export function foo(arg) {
+    switch (arg) {
+        case ENUM_VALUE: {
+            const { data } = arg;
+            call(data);
+            break;
+        }
+        default:
+            break;
+    }
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/4412/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/4412/output.js
new file mode 100644
index 0000000000..9d10be013a
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/4412/output.js
@@ -0,0 +1,6 @@
+export function foo(arg) {
+    if (arg === ENUM_VALUE) {
+        const { data  } = arg;
+        call(data);
+    }
+}