fix(minifier): preserve block statement in labeled iteration statements (#25162)

Preserve block statements inside labeled statements when they contain an
iteration statement (for/while/do-while).
Previously, these blocks were removed, which caused continue label
syntax errors to not be preserved in the minified output:

```js
label: {
  for (let i = 0; i < 10; i++) {
    continue label; // SyntaxError: 'label' does not denote an iteration statement
  }
}
```

fixes #25215

------------------

this guard is broad as we are not checking if inner loop has labeled
continue, this optimization could be implemented but its better if we
wait for #24155

```js
label: for (let i = 0; i < 10; i++) continue label;
```
This commit is contained in:
Armano authored and GitHub committed 2026-08-13 14:49:34 +08:00
1 parent 771d79a9e3
commit e75e102c49
2 files changed
+27 -2

No files matched your search

@@ -43,6 +43,7 @@ impl<'a> PeepholeOptimizations {
|| matches!(first, Statement::FunctionDeclaration(_))
|| (matches!(first, Statement::IfStatement(decl) if decl.alternate.is_some())
&& matches!(ctx.parent(), Ancestor::IfStatementConsequent(_)))
|| (first.is_iteration_statement() && ctx.parent().is_labeled_statement())
{
return;
}
@@ -12,8 +12,8 @@ fn test_break_optimization() {
test("f:while(a())break f;", "f:for(;a();)break f;");
test_same("f:for(x in a())break f");
test("f:{while(a())break;}", "f:for(;a();)break;");
test("f:{for(x in a())break}", "f:for(x in a())break;");
test("f:{while(a())break;}", "f:{for(;a();)break;}");
test("f:{for(x in a())break}", "f:{for(x in a())break;}");
test("f:try{break f;}catch(e){break f;}", "f:try{break f}catch{break f}"); // ;
test(
@@ -29,6 +29,30 @@ fn test_break_optimization() {
test_same("function f() { a: { b(); return; } c(); }");
}
#[test]
fn test_labeled_continue_optimization() {
test("a:while(true)continue a;", "a:for(;;)continue a;"); // a:for(;;);
test(
"x:while(x)y:while(y){if(a)continue x;if(b)continue x;}",
"x:for(;x;)y:for(;y;)if(a||b)continue x;",
);
test(
"x:while(x)y:while(y){if(a)continue y;if(b)continue y;}",
"x:for(;x;)y:for(;y;)if(a||b)continue y;",
); // x:for(;x;)y:for(;y;)a||b;
test(
"x:while(x)y:while(y){if(a)continue x;if(b)continue y}",
"x:for(;x;)y:for(;y;){if(a)continue x;if(b)continue y}",
); // x:for(;x;)y:for(;y;){if(a)continue x;b}
// SyntaxError: Illegal continue statement: 'a' does not denote an iteration statement
test_same("a:if(a()){b();continue a}");
// SyntaxError: Illegal continue statement: no surrounding iteration statement
test_same("a:{for(let i=0;i<10;i++)continue a}");
test("f:{if(true){a();continue f;}else;b();}", "f:{a();continue f}");
// SyntaxError: Undefined label 'a'
test_same("for(let i=0;i<10;i++)continue a;");
}
#[test]
fn test_function_return_optimization() {
test("function f(){return}", "function f(){}");