@@ -31,7 +31,6 @@ use crate::NameBindingKind;
31
31
use rustc_ast as ast;
32
32
use rustc_ast:: visit:: { self , Visitor } ;
33
33
use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap , FxIndexSet } ;
34
- use rustc_data_structures:: unord:: UnordSet ;
35
34
use rustc_errors:: { pluralize, MultiSpan } ;
36
35
use rustc_hir:: def:: { DefKind , Res } ;
37
36
use rustc_session:: lint:: builtin:: { MACRO_USE_EXTERN_CRATE , UNUSED_EXTERN_CRATES , UNUSED_IMPORTS } ;
@@ -43,7 +42,7 @@ struct UnusedImport {
43
42
use_tree : ast:: UseTree ,
44
43
use_tree_id : ast:: NodeId ,
45
44
item_span : Span ,
46
- unused : UnordSet < ast:: NodeId > ,
45
+ unused : FxIndexSet < ast:: NodeId > ,
47
46
}
48
47
49
48
impl UnusedImport {
@@ -96,7 +95,7 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
96
95
// FIXME(#120456) - is `swap_remove` correct?
97
96
self . r . maybe_unused_trait_imports . swap_remove ( & def_id) ;
98
97
if let Some ( i) = self . unused_imports . get_mut ( & self . base_id ) {
99
- i. unused . remove ( & id) ;
98
+ i. unused . swap_remove ( & id) ;
100
99
}
101
100
}
102
101
}
@@ -138,6 +137,16 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
138
137
}
139
138
}
140
139
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
+
141
150
fn report_unused_extern_crate_items (
142
151
& mut self ,
143
152
maybe_unused_extern_crates : FxHashMap < ast:: NodeId , Span > ,
@@ -265,6 +274,40 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> {
265
274
}
266
275
}
267
276
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 ) ]
268
311
enum UnusedSpanResult {
269
312
Used ,
270
313
FlatUnused ( Span , Span ) ,
@@ -412,6 +455,46 @@ impl Resolver<'_, '_> {
412
455
413
456
visitor. report_unused_extern_crate_items ( maybe_unused_extern_crates) ;
414
457
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
+
415
498
for unused in visitor. unused_imports . values ( ) {
416
499
let mut fixes = Vec :: new ( ) ;
417
500
let spans = match calc_unused_spans ( unused, & unused. use_tree , unused. use_tree_id ) {
@@ -432,19 +515,35 @@ impl Resolver<'_, '_> {
432
515
}
433
516
} ;
434
517
435
- let ms = MultiSpan :: from_spans ( spans) ;
518
+ let redundant_sources = unused
519
+ . unused
520
+ . clone ( )
521
+ . into_iter ( )
522
+ . filter_map ( |id| redundant_source_spans. get ( & id) )
523
+ . cloned ( )
524
+ . collect ( ) ;
436
525
437
- let mut span_snippets = ms
526
+ let multi_span = MultiSpan :: from_spans ( spans) ;
527
+ let mut span_snippets = multi_span
438
528
. primary_spans ( )
439
529
. iter ( )
440
530
. filter_map ( |span| tcx. sess . source_map ( ) . span_to_snippet ( * span) . ok ( ) )
441
531
. map ( |s| format ! ( "`{s}`" ) )
442
532
. collect :: < Vec < String > > ( ) ;
443
533
span_snippets. sort ( ) ;
444
534
535
+ let remove_type =
536
+ if unused. unused . iter ( ) . all ( |i| redundant_source_spans. get ( i) . is_none ( ) ) {
537
+ "unused import"
538
+ } else if unused. unused . iter ( ) . all ( |i| redundant_source_spans. get ( i) . is_some ( ) ) {
539
+ "redundant import"
540
+ } else {
541
+ "unused or redundant import"
542
+ } ;
445
543
let msg = format ! (
446
- "unused import{}{}" ,
447
- pluralize!( ms. primary_spans( ) . len( ) ) ,
544
+ "{}{}{}" ,
545
+ remove_type,
546
+ pluralize!( multi_span. primary_spans( ) . len( ) ) ,
448
547
if !span_snippets. is_empty( ) {
449
548
format!( ": {}" , span_snippets. join( ", " ) )
450
549
} else {
@@ -453,11 +552,11 @@ impl Resolver<'_, '_> {
453
552
) ;
454
553
455
554
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"
555
+ "remove the whole `use` item" . to_owned ( )
556
+ } else if multi_span . primary_spans ( ) . len ( ) > 1 {
557
+ format ! ( "remove the {}s" , remove_type )
459
558
} else {
460
- "remove the unused import"
559
+ format ! ( "remove the {}" , remove_type )
461
560
} ;
462
561
463
562
// If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]`
@@ -487,35 +586,15 @@ impl Resolver<'_, '_> {
487
586
visitor. r . lint_buffer . buffer_lint_with_diagnostic (
488
587
UNUSED_IMPORTS ,
489
588
unused. use_tree_id ,
490
- ms ,
589
+ multi_span ,
491
590
msg,
492
- BuiltinLintDiag :: UnusedImports ( fix_msg. into ( ) , fixes, test_module_span) ,
591
+ BuiltinLintDiag :: UnusedImports (
592
+ fix_msg. into ( ) ,
593
+ fixes,
594
+ test_module_span,
595
+ redundant_sources,
596
+ ) ,
493
597
) ;
494
598
}
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
- }
520
599
}
521
600
}
0 commit comments