@@ -586,115 +586,102 @@ impl VisitMut for DeadCodeEliminator {
586
586
}
587
587
}
588
588
589
- // Visit the module to collect references
590
- module. visit_mut_children_with ( self ) ;
589
+ // Process module items
590
+ module. body . visit_mut_with ( self ) ;
591
+
592
+ // Clean up unreferenced import specifiers
593
+ for i in 0 ..module. body . len ( ) {
594
+ if let ModuleItem :: ModuleDecl ( ModuleDecl :: Import ( import) ) = & mut module. body [ i] {
595
+ import. specifiers . retain ( |s| match s {
596
+ ImportSpecifier :: Named ( named) => {
597
+ let imported = match & named. imported {
598
+ Some ( imported) => match imported {
599
+ ModuleExportName :: Ident ( i) => i. sym . clone ( ) ,
600
+ ModuleExportName :: Str ( s) => s. value . clone ( ) ,
601
+ } ,
602
+ None => named. local . sym . clone ( ) ,
603
+ } ;
604
+ let id = ( imported, named. local . ctxt ) ;
605
+ self . import_specifiers . get ( & id) . copied ( ) . unwrap_or ( true )
606
+ }
607
+ ImportSpecifier :: Default ( default) => {
608
+ let id = ( Atom :: from ( "default" ) , default. local . ctxt ) ;
609
+ self . import_specifiers . get ( & id) . copied ( ) . unwrap_or ( true )
610
+ }
611
+ ImportSpecifier :: Namespace ( _) => true , // Always keep namespace imports
612
+ } ) ;
591
613
592
- // Mark used import specifiers
593
- for ( id , var_info ) in & self . vars {
594
- if var_info . referenced && self . import_specifiers . contains_key ( id ) {
595
- if let Some ( used ) = self . import_specifiers . get_mut ( id ) {
596
- * used = true ;
614
+ // Remove empty imports (but keep side-effect imports)
615
+ if import . specifiers . is_empty ( ) && !import . src . value . is_empty ( ) {
616
+ self . changed = true ;
617
+ // Replace with empty statement to maintain AST structure
618
+ module . body [ i ] = ModuleItem :: Stmt ( Stmt :: Empty ( EmptyStmt { span : DUMMY_SP } ) ) ;
597
619
}
598
620
}
599
621
}
600
622
601
- // Second pass: remove unused declarations and unused import specifiers
602
- let mut i = 0 ;
603
- while i < module. body . len ( ) {
604
- let mut removed = false ;
623
+ // Remove unreachable statements in the module
624
+ for i in 0 ..module. body . len ( ) {
625
+ if let ModuleItem :: Stmt ( stmt) = & mut module. body [ i] {
626
+ // Try to remove pure statements that have no side effects
627
+ if self . try_remove_pure_stmt ( stmt) {
628
+ self . changed = true ;
629
+ module. body [ i] = ModuleItem :: Stmt ( Stmt :: Empty ( EmptyStmt { span : DUMMY_SP } ) ) ;
630
+ }
631
+ }
632
+ }
605
633
606
- match & mut module. body [ i] {
607
- ModuleItem :: Stmt ( Stmt :: Decl ( Decl :: Var ( var) ) ) => {
608
- let mut j = 0 ;
609
- while j < var. decls . len ( ) {
610
- let decl = & var. decls [ j] ;
611
- let ids = find_pat_ids ( & decl. name ) ;
634
+ // Exit module scope
635
+ self . exit_scope ( old_scope) ;
636
+ }
612
637
613
- // If all identifiers in this declaration are unused and have no side
614
- // effects, we can remove it
615
- let all_unused = ids. iter ( ) . all ( |id| {
616
- if let Some ( var_info) = self . vars . get ( id) {
617
- !var_info. referenced
618
- && !var_info. has_side_effects
619
- && !var_info. exported
620
- } else {
621
- false
622
- }
623
- } ) ;
638
+ fn visit_mut_script ( & mut self , script : & mut Script ) {
639
+ // Initialize the top-level script scope
640
+ let old_scope = self . enter_scope ( None ) ;
641
+ if let Some ( scope) = self . scopes . last_mut ( ) {
642
+ scope. is_module_scope = true ;
643
+ }
624
644
625
- let no_side_effects = decl
645
+ // First pass: collect all declarations
646
+ for stmt in & script. body {
647
+ match stmt {
648
+ Stmt :: Decl ( Decl :: Var ( var) ) => {
649
+ for decl in & var. decls {
650
+ let ids = find_pat_ids ( & decl. name ) ;
651
+ let has_effects = decl
626
652
. init
627
653
. as_ref ( )
628
- . map_or ( true , |init| self . is_pure_expr ( init) ) ;
629
-
630
- if all_unused && no_side_effects {
631
- var. decls . remove ( j) ;
632
- self . changed = true ;
633
- removed = var. decls . is_empty ( ) ;
634
- } else {
635
- j += 1 ;
654
+ . map_or ( false , |init| !self . is_pure_expr ( init) ) ;
655
+ for id in ids {
656
+ // Mark all top-level declarations in scripts as referenced
657
+ self . register_reference ( & id) ;
658
+ self . register_declaration ( id, has_effects, false ) ;
636
659
}
637
660
}
638
661
}
639
- ModuleItem :: Stmt ( Stmt :: Decl ( Decl :: Fn ( fn_decl) ) ) => {
662
+ Stmt :: Decl ( Decl :: Fn ( fn_decl) ) => {
640
663
let id = ( fn_decl. ident . sym . clone ( ) , fn_decl. ident . ctxt ) ;
641
- if let Some ( var_info) = self . vars . get ( & id) {
642
- if !var_info. referenced && !var_info. exported {
643
- module. body . remove ( i) ;
644
- self . changed = true ;
645
- removed = true ;
646
- }
647
- }
664
+ // Mark all top-level declarations in scripts as referenced
665
+ self . register_reference ( & id) ;
666
+ self . register_declaration ( id, false , true ) ;
648
667
}
649
- ModuleItem :: Stmt ( Stmt :: Decl ( Decl :: Class ( class_decl) ) ) => {
668
+ Stmt :: Decl ( Decl :: Class ( class_decl) ) => {
650
669
let id = ( class_decl. ident . sym . clone ( ) , class_decl. ident . ctxt ) ;
651
- if let Some ( var_info) = self . vars . get ( & id) {
652
- if !var_info. referenced && !var_info. exported {
653
- module. body . remove ( i) ;
654
- self . changed = true ; // 클래스 제거 시 명시적으로 changed 설정
655
- removed = true ;
656
- }
657
- }
658
- }
659
- ModuleItem :: Stmt ( stmt) => {
660
- self . try_remove_pure_stmt ( stmt) ;
661
- }
662
- ModuleItem :: ModuleDecl ( ModuleDecl :: Import ( import_decl) ) => {
663
- // Store original count to check if we've removed all specifiers
664
- let original_count = import_decl. specifiers . len ( ) ;
665
-
666
- // Remove unused import specifiers
667
- let mut removed_any = false ;
668
- import_decl. specifiers . retain ( |specifier| {
669
- let local = match specifier {
670
- ImportSpecifier :: Named ( named) => & named. local ,
671
- ImportSpecifier :: Default ( default) => & default. local ,
672
- ImportSpecifier :: Namespace ( namespace) => & namespace. local ,
673
- } ;
674
-
675
- let id = ( local. sym . clone ( ) , local. ctxt ) ;
676
- // Keep the specifier if it's used or not tracked
677
- let keep = self . import_specifiers . get ( & id) . map_or ( true , |used| * used) ;
678
- if !keep {
679
- removed_any = true ;
680
- }
681
- keep
682
- } ) ;
683
-
684
- // 정말로 import specifier가 제거되었는지 확인하고 변경 사항 표시
685
- if removed_any && original_count != import_decl. specifiers . len ( ) {
686
- self . changed = true ;
687
- }
670
+ // Mark all top-level declarations in scripts as referenced
671
+ self . register_reference ( & id) ;
672
+ self . register_declaration ( id, false , false ) ;
688
673
}
689
674
_ => { }
690
675
}
691
-
692
- if !removed {
693
- i += 1 ;
694
- }
695
676
}
696
677
697
- // Exit module scope
678
+ // Visit the script to process nested statements and expressions
679
+ script. visit_mut_children_with ( self ) ;
680
+
681
+ // Remove unreachable statements in the script
682
+ self . remove_unreachable_stmts ( & mut script. body ) ;
683
+
684
+ // Exit script scope
698
685
self . exit_scope ( old_scope) ;
699
686
}
700
687
0 commit comments