diff --git a/crates/oxc_ast/src/ast_builder_impl.rs b/crates/oxc_ast/src/ast_builder_impl.rs index 69c1e636e4f53..ac4d12b6cad3c 100644 --- a/crates/oxc_ast/src/ast_builder_impl.rs +++ b/crates/oxc_ast/src/ast_builder_impl.rs @@ -112,6 +112,20 @@ impl<'a> AstBuilder<'a> { mem::replace(decl, empty_decl) } + #[inline] + pub fn move_variable_declaration( + self, + decl: &mut VariableDeclaration<'a>, + ) -> VariableDeclaration<'a> { + let empty_decl = self.variable_declaration( + Span::default(), + VariableDeclarationKind::Var, + self.vec(), + false, + ); + mem::replace(decl, empty_decl) + } + #[inline] pub fn move_vec(self, vec: &mut Vec<'a, T>) -> Vec<'a, T> { mem::replace(vec, self.vec()) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs index 85fe9dd7d9e91..ed253c298c9b5 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_remove_dead_code.rs @@ -203,11 +203,26 @@ impl<'a, 'b> PeepholeRemoveDeadCode { // Check vars in statement let mut keep_var = KeepVar::new(ctx.ast); keep_var.visit_statement(&for_stmt.body); - Some( - keep_var - .get_variable_declaration_statement() - .unwrap_or_else(|| ctx.ast.statement_empty(SPAN)), - ) + + let mut var_decl = keep_var.get_variable_declaration(); + + if let Some(ForStatementInit::VariableDeclaration(var_init)) = &mut for_stmt.init { + if var_init.kind.is_var() { + if let Some(var_decl) = &mut var_decl { + var_decl + .declarations + .splice(0..0, ctx.ast.move_vec(&mut var_init.declarations)); + } else { + var_decl = Some(ctx.ast.move_variable_declaration(var_init)); + } + } + } + + var_decl + .map(|var_decl| { + ctx.ast.statement_declaration(ctx.ast.declaration_from_variable(var_decl)) + }) + .or_else(|| Some(ctx.ast.statement_empty(SPAN))) } Some(true) => { // Remove the test expression. @@ -350,6 +365,10 @@ mod test { // Make sure it plays nice with minimizing fold("for(;false;) { foo(); continue }", ""); + fold("for (var { c, x: [d] } = {}; 0;);", "var { c, x: [d] } = {};"); + fold("for (var se = [1, 2]; false;);", "var se = [1, 2];"); + fold("for (var se = [1, 2]; false;) { var a = 0; }", "var se = [1, 2], a;"); + // fold("l1:for(;false;) { }", ""); } diff --git a/crates/oxc_minifier/src/keep_var.rs b/crates/oxc_minifier/src/keep_var.rs index 6652e1e14d538..b66c0f3c861ca 100644 --- a/crates/oxc_minifier/src/keep_var.rs +++ b/crates/oxc_minifier/src/keep_var.rs @@ -34,19 +34,21 @@ impl<'a> Visit<'a> for KeepVar<'a> { // match_module_declaration!(Statement) => { // visitor.visit_module_declaration(it.to_module_declaration()) // } - Statement::VariableDeclaration(decl) => { - if decl.kind.is_var() { - decl.bound_names(&mut |ident| { - self.vars.push((ident.name.clone(), ident.span)); - }); - if decl.has_init() { - self.all_hoisted = false; - } - } - } + Statement::VariableDeclaration(decl) => self.visit_variable_declaration(decl), _ => {} } } + + fn visit_variable_declaration(&mut self, it: &VariableDeclaration<'a>) { + if it.kind.is_var() { + it.bound_names(&mut |ident| { + self.vars.push((ident.name.clone(), ident.span)); + }); + if it.has_init() { + self.all_hoisted = false; + } + } + } } impl<'a> KeepVar<'a> { @@ -58,7 +60,7 @@ impl<'a> KeepVar<'a> { self.all_hoisted } - pub fn get_variable_declaration_statement(self) -> Option> { + pub fn get_variable_declaration(self) -> Option> { if self.vars.is_empty() { return None; } @@ -71,7 +73,13 @@ impl<'a> KeepVar<'a> { })); let decl = self.ast.variable_declaration(SPAN, kind, decls, false); - let stmt = self.ast.statement_declaration(self.ast.declaration_from_variable(decl)); + Some(decl) + } + + pub fn get_variable_declaration_statement(self) -> Option> { + let stmt = self.ast.statement_declaration( + self.ast.declaration_from_variable(self.get_variable_declaration()?), + ); Some(stmt) } }