@@ -29,11 +29,12 @@ use crate::Resolver;
29
29
30
30
use rustc_ast as ast;
31
31
use rustc_ast:: visit:: { self , Visitor } ;
32
- use rustc_data_structures:: fx:: FxIndexMap ;
32
+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap } ;
33
33
use rustc_data_structures:: unord:: UnordSet ;
34
34
use rustc_errors:: { pluralize, MultiSpan } ;
35
- use rustc_session:: lint:: builtin:: { MACRO_USE_EXTERN_CRATE , UNUSED_IMPORTS } ;
35
+ use rustc_session:: lint:: builtin:: { MACRO_USE_EXTERN_CRATE , UNUSED_EXTERN_CRATES , UNUSED_IMPORTS } ;
36
36
use rustc_session:: lint:: BuiltinLintDiagnostics ;
37
+ use rustc_span:: symbol:: Ident ;
37
38
use rustc_span:: { Span , DUMMY_SP } ;
38
39
39
40
struct UnusedImport < ' a > {
@@ -53,11 +54,28 @@ struct UnusedImportCheckVisitor<'a, 'b, 'tcx> {
53
54
r : & ' a mut Resolver < ' b , ' tcx > ,
54
55
/// All the (so far) unused imports, grouped path list
55
56
unused_imports : FxIndexMap < ast:: NodeId , UnusedImport < ' a > > ,
57
+ extern_crate_items : Vec < ExternCrateToLint > ,
56
58
base_use_tree : Option < & ' a ast:: UseTree > ,
57
59
base_id : ast:: NodeId ,
58
60
item_span : Span ,
59
61
}
60
62
63
+ struct ExternCrateToLint {
64
+ id : ast:: NodeId ,
65
+ /// Span from the item
66
+ span : Span ,
67
+ /// Span to use to suggest complete removal.
68
+ span_with_attributes : Span ,
69
+ /// Span of the visibility, if any.
70
+ vis_span : Span ,
71
+ /// Whether the item has attrs.
72
+ has_attrs : bool ,
73
+ /// Name used to refer to the crate.
74
+ ident : Ident ,
75
+ /// Whether the statement renames the crate `extern crate orig_name as new_name;`.
76
+ renames : bool ,
77
+ }
78
+
61
79
impl < ' a , ' b , ' tcx > UnusedImportCheckVisitor < ' a , ' b , ' tcx > {
62
80
// We have information about whether `use` (import) items are actually
63
81
// used now. If an import is not used at all, we signal a lint error.
@@ -96,18 +114,27 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
96
114
97
115
impl < ' a , ' b , ' tcx > Visitor < ' a > for UnusedImportCheckVisitor < ' a , ' b , ' tcx > {
98
116
fn visit_item ( & mut self , item : & ' a ast:: Item ) {
99
- self . item_span = item. span_with_attributes ( ) ;
100
-
101
- // Ignore is_public import statements because there's no way to be sure
102
- // whether they're used or not. Also ignore imports with a dummy span
103
- // because this means that they were generated in some fashion by the
104
- // compiler and we don't need to consider them.
105
- if let ast:: ItemKind :: Use ( ..) = item. kind {
106
- if item. vis . kind . is_pub ( ) || item. span . is_dummy ( ) {
107
- return ;
117
+ match item. kind {
118
+ // Ignore is_public import statements because there's no way to be sure
119
+ // whether they're used or not. Also ignore imports with a dummy span
120
+ // because this means that they were generated in some fashion by the
121
+ // compiler and we don't need to consider them.
122
+ ast:: ItemKind :: Use ( ..) if item. vis . kind . is_pub ( ) || item. span . is_dummy ( ) => return ,
123
+ ast:: ItemKind :: ExternCrate ( orig_name) => {
124
+ self . extern_crate_items . push ( ExternCrateToLint {
125
+ id : item. id ,
126
+ span : item. span ,
127
+ vis_span : item. vis . span ,
128
+ span_with_attributes : item. span_with_attributes ( ) ,
129
+ has_attrs : !item. attrs . is_empty ( ) ,
130
+ ident : item. ident ,
131
+ renames : orig_name. is_some ( ) ,
132
+ } ) ;
108
133
}
134
+ _ => { }
109
135
}
110
136
137
+ self . item_span = item. span_with_attributes ( ) ;
111
138
visit:: walk_item ( self , item) ;
112
139
}
113
140
@@ -224,6 +251,9 @@ fn calc_unused_spans(
224
251
225
252
impl Resolver < ' _ , ' _ > {
226
253
pub ( crate ) fn check_unused ( & mut self , krate : & ast:: Crate ) {
254
+ let tcx = self . tcx ;
255
+ let mut maybe_unused_extern_crates = FxHashMap :: default ( ) ;
256
+
227
257
for import in self . potentially_unused_imports . iter ( ) {
228
258
match import. kind {
229
259
_ if import. used . get ( )
@@ -246,7 +276,14 @@ impl Resolver<'_, '_> {
246
276
}
247
277
ImportKind :: ExternCrate { id, .. } => {
248
278
let def_id = self . local_def_id ( id) ;
249
- self . maybe_unused_extern_crates . push ( ( def_id, import. span ) ) ;
279
+ if self . extern_crate_map . get ( & def_id) . map_or ( true , |& cnum| {
280
+ !tcx. is_compiler_builtins ( cnum)
281
+ && !tcx. is_panic_runtime ( cnum)
282
+ && !tcx. has_global_allocator ( cnum)
283
+ && !tcx. has_panic_handler ( cnum)
284
+ } ) {
285
+ maybe_unused_extern_crates. insert ( id, import. span ) ;
286
+ }
250
287
}
251
288
ImportKind :: MacroUse => {
252
289
let msg = "unused `#[macro_use]` import" ;
@@ -259,6 +296,7 @@ impl Resolver<'_, '_> {
259
296
let mut visitor = UnusedImportCheckVisitor {
260
297
r : self ,
261
298
unused_imports : Default :: default ( ) ,
299
+ extern_crate_items : Default :: default ( ) ,
262
300
base_use_tree : None ,
263
301
base_id : ast:: DUMMY_NODE_ID ,
264
302
item_span : DUMMY_SP ,
@@ -290,7 +328,7 @@ impl Resolver<'_, '_> {
290
328
let ms = MultiSpan :: from_spans ( spans. clone ( ) ) ;
291
329
let mut span_snippets = spans
292
330
. iter ( )
293
- . filter_map ( |s| match visitor . r . tcx . sess . source_map ( ) . span_to_snippet ( * s) {
331
+ . filter_map ( |s| match tcx. sess . source_map ( ) . span_to_snippet ( * s) {
294
332
Ok ( s) => Some ( format ! ( "`{}`" , s) ) ,
295
333
_ => None ,
296
334
} )
@@ -317,7 +355,7 @@ impl Resolver<'_, '_> {
317
355
// If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]`
318
356
// attribute; however, if not, suggest adding the attribute. There is no way to
319
357
// retrieve attributes here because we do not have a `TyCtxt` yet.
320
- let test_module_span = if visitor . r . tcx . sess . opts . test {
358
+ let test_module_span = if tcx. sess . opts . test {
321
359
None
322
360
} else {
323
361
let parent_module = visitor. r . get_nearest_non_block_module (
@@ -346,5 +384,74 @@ impl Resolver<'_, '_> {
346
384
BuiltinLintDiagnostics :: UnusedImports ( fix_msg. into ( ) , fixes, test_module_span) ,
347
385
) ;
348
386
}
387
+
388
+ for extern_crate in visitor. extern_crate_items {
389
+ let warn_if_unused = !extern_crate. ident . name . as_str ( ) . starts_with ( '_' ) ;
390
+
391
+ // If the crate is fully unused, we suggest removing it altogether.
392
+ // We do this in any edition.
393
+ if warn_if_unused {
394
+ if let Some ( & span) = maybe_unused_extern_crates. get ( & extern_crate. id ) {
395
+ visitor. r . lint_buffer . buffer_lint_with_diagnostic (
396
+ UNUSED_EXTERN_CRATES ,
397
+ extern_crate. id ,
398
+ span,
399
+ "unused extern crate" ,
400
+ BuiltinLintDiagnostics :: UnusedExternCrate {
401
+ removal_span : extern_crate. span_with_attributes ,
402
+ } ,
403
+ ) ;
404
+ continue ;
405
+ }
406
+ }
407
+
408
+ // If we are not in Rust 2018 edition, then we don't make any further
409
+ // suggestions.
410
+ if !tcx. sess . rust_2018 ( ) {
411
+ continue ;
412
+ }
413
+
414
+ // If the extern crate has any attributes, they may have funky
415
+ // semantics we can't faithfully represent using `use` (most
416
+ // notably `#[macro_use]`). Ignore it.
417
+ if extern_crate. has_attrs {
418
+ continue ;
419
+ }
420
+
421
+ // If the extern crate is renamed, then we cannot suggest replacing it with a use as this
422
+ // would not insert the new name into the prelude, where other imports in the crate may be
423
+ // expecting it.
424
+ if extern_crate. renames {
425
+ continue ;
426
+ }
427
+
428
+ // If the extern crate isn't in the extern prelude,
429
+ // there is no way it can be written as a `use`.
430
+ if !visitor
431
+ . r
432
+ . extern_prelude
433
+ . get ( & extern_crate. ident )
434
+ . map_or ( false , |entry| !entry. introduced_by_item )
435
+ {
436
+ continue ;
437
+ }
438
+
439
+ let vis_span = extern_crate
440
+ . vis_span
441
+ . find_ancestor_inside ( extern_crate. span )
442
+ . unwrap_or ( extern_crate. vis_span ) ;
443
+ let ident_span = extern_crate
444
+ . ident
445
+ . span
446
+ . find_ancestor_inside ( extern_crate. span )
447
+ . unwrap_or ( extern_crate. ident . span ) ;
448
+ visitor. r . lint_buffer . buffer_lint_with_diagnostic (
449
+ UNUSED_EXTERN_CRATES ,
450
+ extern_crate. id ,
451
+ extern_crate. span ,
452
+ "`extern crate` is not idiomatic in the new edition" ,
453
+ BuiltinLintDiagnostics :: ExternCrateNotIdiomatic { vis_span, ident_span } ,
454
+ ) ;
455
+ }
349
456
}
350
457
}
0 commit comments