@@ -31,7 +31,6 @@ use crate::NameBindingKind;
3131use  rustc_ast as  ast; 
3232use  rustc_ast:: visit:: { self ,  Visitor } ; 
3333use  rustc_data_structures:: fx:: { FxHashMap ,  FxIndexMap ,  FxIndexSet } ; 
34- use  rustc_data_structures:: unord:: UnordSet ; 
3534use  rustc_errors:: { pluralize,  MultiSpan } ; 
3635use  rustc_hir:: def:: { DefKind ,  Res } ; 
3736use  rustc_session:: lint:: builtin:: { MACRO_USE_EXTERN_CRATE ,  UNUSED_EXTERN_CRATES ,  UNUSED_IMPORTS } ; 
@@ -43,7 +42,7 @@ struct UnusedImport {
4342    use_tree :  ast:: UseTree , 
4443    use_tree_id :  ast:: NodeId , 
4544    item_span :  Span , 
46-     unused :  UnordSet < ast:: NodeId > , 
45+     unused :  FxIndexSet < ast:: NodeId > , 
4746} 
4847
4948impl  UnusedImport  { 
@@ -96,7 +95,7 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
9695            // FIXME(#120456) - is `swap_remove` correct? 
9796            self . r . maybe_unused_trait_imports . swap_remove ( & def_id) ; 
9897            if  let  Some ( i)  = self . unused_imports . get_mut ( & self . base_id )  { 
99-                 i. unused . remove ( & id) ; 
98+                 i. unused . swap_remove ( & id) ; 
10099            } 
101100        } 
102101    } 
@@ -138,6 +137,16 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
138137        } 
139138    } 
140139
140+     fn  merge_unused_import ( & mut  self ,  unused_import :  UnusedImport )  { 
141+         if  let  Some ( import)  = self . unused_imports . get_mut ( & unused_import. use_tree_id )  { 
142+             for  id in  unused_import. unused  { 
143+                 import. unused . insert ( id) ; 
144+             } 
145+         }  else  { 
146+             self . unused_imports . entry ( unused_import. use_tree_id ) . or_insert ( unused_import) ; 
147+         } 
148+     } 
149+ 
141150    fn  report_unused_extern_crate_items ( 
142151        & mut  self , 
143152        maybe_unused_extern_crates :  FxHashMap < ast:: NodeId ,  Span > , 
@@ -265,6 +274,40 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> {
265274    } 
266275} 
267276
277+ struct  ImportFinderVisitor  { 
278+     unused_import :  Option < UnusedImport > , 
279+     root_node_id :  ast:: NodeId , 
280+     node_id :  ast:: NodeId , 
281+     item_span :  Span , 
282+ } 
283+ 
284+ impl  Visitor < ' _ >  for  ImportFinderVisitor  { 
285+     fn  visit_item ( & mut  self ,  item :  & ast:: Item )  { 
286+         match  item. kind  { 
287+             ast:: ItemKind :: Use ( ..)  if  item. span . is_dummy ( )  => return , 
288+             _ => { } 
289+         } 
290+ 
291+         self . item_span  = item. span_with_attributes ( ) ; 
292+         visit:: walk_item ( self ,  item) ; 
293+     } 
294+ 
295+     fn  visit_use_tree ( & mut  self ,  use_tree :  & ast:: UseTree ,  id :  ast:: NodeId ,  _nested :  bool )  { 
296+         if  id == self . root_node_id  { 
297+             let  mut  unused_import = UnusedImport  { 
298+                 use_tree :  use_tree. clone ( ) , 
299+                 use_tree_id :  id, 
300+                 item_span :  self . item_span , 
301+                 unused :  Default :: default ( ) , 
302+             } ; 
303+             unused_import. unused . insert ( self . node_id ) ; 
304+             self . unused_import  = Some ( unused_import) ; 
305+         } 
306+         visit:: walk_use_tree ( self ,  use_tree,  id) ; 
307+     } 
308+ } 
309+ 
310+ #[ derive( Debug ) ]  
268311enum  UnusedSpanResult  { 
269312    Used , 
270313    FlatUnused ( Span ,  Span ) , 
@@ -412,6 +455,46 @@ impl Resolver<'_, '_> {
412455
413456        visitor. report_unused_extern_crate_items ( maybe_unused_extern_crates) ; 
414457
458+         let  unused_imports = & visitor. unused_imports ; 
459+         let  mut  check_redundant_imports = FxIndexSet :: default ( ) ; 
460+         for  module in  visitor. r . arenas . local_modules ( ) . iter ( )  { 
461+             for  ( _key,  resolution)  in  visitor. r . resolutions ( * module) . borrow ( ) . iter ( )  { 
462+                 let  resolution = resolution. borrow ( ) ; 
463+ 
464+                 if  let  Some ( binding)  = resolution. binding 
465+                     && let  NameBindingKind :: Import  {  import,  .. }  = binding. kind 
466+                     && let  ImportKind :: Single  {  id,  .. }  = import. kind 
467+                 { 
468+                     if  let  Some ( unused_import)  = unused_imports. get ( & import. root_id ) 
469+                         && unused_import. unused . contains ( & id) 
470+                     { 
471+                         continue ; 
472+                     } 
473+ 
474+                     check_redundant_imports. insert ( import) ; 
475+                 } 
476+             } 
477+         } 
478+ 
479+         let  mut  redundant_source_spans = FxHashMap :: default ( ) ; 
480+         for  import in  check_redundant_imports { 
481+             if  let  Some ( redundant_spans)  = visitor. r . check_for_redundant_imports ( import) 
482+                 && let  ImportKind :: Single  {  source,  id,  .. }  = import. kind 
483+             { 
484+                 let  mut  finder = ImportFinderVisitor  { 
485+                     node_id :  id, 
486+                     root_node_id :  import. root_id , 
487+                     unused_import :  None , 
488+                     item_span :  Span :: default ( ) , 
489+                 } ; 
490+                 visit:: walk_crate ( & mut  finder,  krate) ; 
491+                 if  let  Some ( unused)  = finder. unused_import  { 
492+                     visitor. merge_unused_import ( unused) ; 
493+                     redundant_source_spans. insert ( id,  ( source,  redundant_spans) ) ; 
494+                 } 
495+             } 
496+         } 
497+ 
415498        for  unused in  visitor. unused_imports . values ( )  { 
416499            let  mut  fixes = Vec :: new ( ) ; 
417500            let  spans = match  calc_unused_spans ( unused,  & unused. use_tree ,  unused. use_tree_id )  { 
@@ -432,19 +515,34 @@ impl Resolver<'_, '_> {
432515                } 
433516            } ; 
434517
435-             let  ms = MultiSpan :: from_spans ( spans) ; 
518+             let  mut  redundant_sources = vec ! [ ] ; 
519+             for  id in  & unused. unused  { 
520+                 if  let  Some ( source)  = redundant_source_spans. get ( id)  { 
521+                     redundant_sources. push ( source. clone ( ) ) ; 
522+                 } 
523+             } 
436524
437-             let  mut  span_snippets = ms
525+             let  multi_span = MultiSpan :: from_spans ( spans) ; 
526+             let  mut  span_snippets = multi_span
438527                . primary_spans ( ) 
439528                . iter ( ) 
440529                . filter_map ( |span| tcx. sess . source_map ( ) . span_to_snippet ( * span) . ok ( ) ) 
441530                . map ( |s| format ! ( "`{s}`" ) ) 
442531                . collect :: < Vec < String > > ( ) ; 
443532            span_snippets. sort ( ) ; 
444533
534+             let  remove_type =
535+                 if  unused. unused . iter ( ) . all ( |i| redundant_source_spans. get ( i) . is_none ( ) )  { 
536+                     "unused import" 
537+                 }  else  if  unused. unused . iter ( ) . all ( |i| redundant_source_spans. get ( i) . is_some ( ) )  { 
538+                     "redundant import" 
539+                 }  else  { 
540+                     "unused or redundant import" 
541+                 } ; 
445542            let  msg = format ! ( 
446-                 "unused import{}{}" , 
447-                 pluralize!( ms. primary_spans( ) . len( ) ) , 
543+                 "{}{}{}" , 
544+                 remove_type, 
545+                 pluralize!( multi_span. primary_spans( ) . len( ) ) , 
448546                if  !span_snippets. is_empty( )  { 
449547                    format!( ": {}" ,  span_snippets. join( ", " ) ) 
450548                }  else { 
@@ -453,11 +551,11 @@ impl Resolver<'_, '_> {
453551            ) ; 
454552
455553            let  fix_msg = if  fixes. len ( )  == 1  && fixes[ 0 ] . 0  == unused. item_span  { 
456-                 "remove the whole `use` item" 
457-             }  else  if  ms . primary_spans ( ) . len ( )  > 1  { 
458-                 "remove the unused imports"  
554+                 "remove the whole `use` item" . to_owned ( ) 
555+             }  else  if  multi_span . primary_spans ( ) . len ( )  > 1  { 
556+                 format ! ( "remove the {}s"  ,  remove_type ) 
459557            }  else  { 
460-                 "remove the unused import"  
558+                 format ! ( "remove the {}"  ,  remove_type ) 
461559            } ; 
462560
463561            // If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]` 
@@ -487,35 +585,15 @@ impl Resolver<'_, '_> {
487585            visitor. r . lint_buffer . buffer_lint_with_diagnostic ( 
488586                UNUSED_IMPORTS , 
489587                unused. use_tree_id , 
490-                 ms , 
588+                 multi_span , 
491589                msg, 
492-                 BuiltinLintDiag :: UnusedImports ( fix_msg. into ( ) ,  fixes,  test_module_span) , 
590+                 BuiltinLintDiag :: UnusedImports ( 
591+                     fix_msg. into ( ) , 
592+                     fixes, 
593+                     test_module_span, 
594+                     redundant_sources, 
595+                 ) , 
493596            ) ; 
494597        } 
495- 
496-         let  unused_imports = visitor. unused_imports ; 
497-         let  mut  check_redundant_imports = FxIndexSet :: default ( ) ; 
498-         for  module in  self . arenas . local_modules ( ) . iter ( )  { 
499-             for  ( _key,  resolution)  in  self . resolutions ( * module) . borrow ( ) . iter ( )  { 
500-                 let  resolution = resolution. borrow ( ) ; 
501- 
502-                 if  let  Some ( binding)  = resolution. binding 
503-                     && let  NameBindingKind :: Import  {  import,  .. }  = binding. kind 
504-                     && let  ImportKind :: Single  {  id,  .. }  = import. kind 
505-                 { 
506-                     if  let  Some ( unused_import)  = unused_imports. get ( & import. root_id ) 
507-                         && unused_import. unused . contains ( & id) 
508-                     { 
509-                         continue ; 
510-                     } 
511- 
512-                     check_redundant_imports. insert ( import) ; 
513-                 } 
514-             } 
515-         } 
516- 
517-         for  import in  check_redundant_imports { 
518-             self . check_for_redundant_imports ( import) ; 
519-         } 
520598    } 
521599} 
0 commit comments