@@ -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 . clone ( ) {
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