@@ -8,7 +8,7 @@ use oxc_semantic::ScopeId;
88use oxc_span:: { ContentEq , GetSpan } ;
99use oxc_traverse:: Ancestor ;
1010
11- use crate :: { ctx:: Ctx , keep_var:: KeepVar } ;
11+ use crate :: { CompressOptionsUnused , ctx:: Ctx , keep_var:: KeepVar } ;
1212
1313use super :: { PeepholeOptimizations , State } ;
1414
@@ -303,7 +303,7 @@ impl<'a> PeepholeOptimizations {
303303 result. push ( Statement :: ContinueStatement ( s) ) ;
304304 }
305305 Statement :: VariableDeclaration ( var_decl) => {
306- self . handle_variable_declaration ( var_decl, result, state) ;
306+ self . handle_variable_declaration ( var_decl, result, state, ctx ) ;
307307 }
308308 Statement :: ExpressionStatement ( expr_stmt) => {
309309 self . handle_expression_statement ( expr_stmt, result, state, ctx) ;
@@ -367,20 +367,59 @@ impl<'a> PeepholeOptimizations {
367367 false
368368 }
369369
370+ /// For variable declarations:
371+ /// * merge with the previous variable declarator if their kinds are the same
372+ /// * remove the variable declarator if it is unused
373+ /// * keep the initializer if it has side effects
370374 fn handle_variable_declaration (
371375 & self ,
372- mut var_decl : Box < ' a , VariableDeclaration < ' a > > ,
376+ var_decl : Box < ' a , VariableDeclaration < ' a > > ,
373377 result : & mut Vec < ' a , Statement < ' a > > ,
374378 state : & mut State ,
379+ ctx : & mut Ctx < ' a , ' _ > ,
375380 ) {
376- if let Some ( Statement :: VariableDeclaration ( prev_var_decl) ) = result. last_mut ( ) {
381+ if let Some ( Statement :: VariableDeclaration ( prev_var_decl) ) = result. last ( ) {
377382 if var_decl. kind == prev_var_decl. kind {
378- prev_var_decl. declarations . extend ( var_decl. declarations . drain ( ..) ) ;
379383 state. changed = true ;
380- return ;
381384 }
382385 }
383- result. push ( Statement :: VariableDeclaration ( var_decl) ) ;
386+ let VariableDeclaration { span, kind, declarations, declare } = var_decl. unbox ( ) ;
387+ for mut decl in declarations {
388+ if Self :: is_declarator_unused ( & decl, ctx) {
389+ state. changed = true ;
390+ if let Some ( init) = decl. init . take ( ) {
391+ if init. may_have_side_effects ( ctx) {
392+ result. push ( ctx. ast . statement_expression ( init. span ( ) , init) ) ;
393+ }
394+ }
395+ } else {
396+ if let Some ( Statement :: VariableDeclaration ( prev_var_decl) ) = result. last_mut ( ) {
397+ if kind == prev_var_decl. kind {
398+ prev_var_decl. declarations . push ( decl) ;
399+ continue ;
400+ }
401+ }
402+ let decls = ctx. ast . vec1 ( decl) ;
403+ let new_decl = ctx. ast . alloc_variable_declaration ( span, kind, decls, declare) ;
404+ result. push ( Statement :: VariableDeclaration ( new_decl) ) ;
405+ }
406+ }
407+ }
408+
409+ fn is_declarator_unused ( decl : & VariableDeclarator < ' a > , ctx : & mut Ctx < ' a , ' _ > ) -> bool {
410+ if ctx. state . options . unused == CompressOptionsUnused :: Keep {
411+ return false ;
412+ }
413+ // It is unsafe to remove if direct eval is involved.
414+ if ctx. scoping ( ) . root_scope_flags ( ) . contains_direct_eval ( ) {
415+ return false ;
416+ }
417+ if let BindingPatternKind :: BindingIdentifier ( ident) = & decl. id . kind {
418+ if let Some ( symbol_id) = ident. symbol_id . get ( ) {
419+ return ctx. scoping ( ) . symbol_is_unused ( symbol_id) ;
420+ }
421+ }
422+ false
384423 }
385424
386425 fn handle_expression_statement (
0 commit comments