]>
Commit | Line | Data |
---|---|---|
f2b60f7d | 1 | use crate::errors::{ExternCrateNotIdiomatic, UnusedExternCrate}; |
2b03887a FG |
2 | use rustc_data_structures::fx::FxHashMap; |
3 | use rustc_data_structures::unord::UnordSet; | |
dfeec247 | 4 | use rustc_hir as hir; |
04454e1e | 5 | use rustc_hir::def::DefKind; |
17df50a5 | 6 | use rustc_hir::def_id::{DefId, LocalDefId}; |
ba9703b0 XL |
7 | use rustc_middle::ty::TyCtxt; |
8 | use rustc_session::lint; | |
f9f354fc | 9 | use rustc_span::{Span, Symbol}; |
94b46f34 | 10 | |
416331ca | 11 | pub fn check_crate(tcx: TyCtxt<'_>) { |
2b03887a | 12 | let mut used_trait_imports: UnordSet<LocalDefId> = Default::default(); |
04454e1e | 13 | |
c295e0f8 | 14 | for item_def_id in tcx.hir().body_owners() { |
f9f354fc | 15 | let imports = tcx.used_trait_imports(item_def_id); |
94b46f34 | 16 | debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports); |
2b03887a | 17 | used_trait_imports.extend(imports.items().copied()); |
94b46f34 XL |
18 | } |
19 | ||
923072b8 FG |
20 | for &id in tcx.maybe_unused_trait_imports(()) { |
21 | debug_assert_eq!(tcx.def_kind(id), DefKind::Use); | |
22 | if tcx.visibility(id).is_public() { | |
23 | continue; | |
24 | } | |
25 | if used_trait_imports.contains(&id) { | |
26 | continue; | |
27 | } | |
28 | let item = tcx.hir().expect_item(id); | |
29 | if item.span.is_dummy() { | |
30 | continue; | |
94b46f34 | 31 | } |
923072b8 | 32 | let hir::ItemKind::Use(path, _) = item.kind else { unreachable!() }; |
2b03887a FG |
33 | let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { |
34 | format!("unused import: `{}`", snippet) | |
35 | } else { | |
36 | "unused import".to_owned() | |
37 | }; | |
38 | tcx.struct_span_lint_hir( | |
39 | lint::builtin::UNUSED_IMPORTS, | |
40 | item.hir_id(), | |
41 | path.span, | |
42 | msg, | |
43 | |lint| lint, | |
44 | ); | |
94b46f34 XL |
45 | } |
46 | ||
04454e1e | 47 | unused_crates_lint(tcx); |
94b46f34 XL |
48 | } |
49 | ||
416331ca | 50 | fn unused_crates_lint(tcx: TyCtxt<'_>) { |
94b46f34 XL |
51 | let lint = lint::builtin::UNUSED_EXTERN_CRATES; |
52 | ||
6522a427 | 53 | // Collect first the crates that are completely unused. These we |
94b46f34 XL |
54 | // can always suggest removing (no matter which edition we are |
55 | // in). | |
f9f354fc | 56 | let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx |
17df50a5 | 57 | .maybe_unused_extern_crates(()) |
94b46f34 | 58 | .iter() |
94b46f34 | 59 | .filter(|&&(def_id, _)| { |
a1dfa0c6 | 60 | tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| { |
dfeec247 XL |
61 | !tcx.is_compiler_builtins(cnum) |
62 | && !tcx.is_panic_runtime(cnum) | |
63 | && !tcx.has_global_allocator(cnum) | |
64 | && !tcx.has_panic_handler(cnum) | |
a1dfa0c6 | 65 | }) |
94b46f34 XL |
66 | }) |
67 | .cloned() | |
68 | .collect(); | |
69 | ||
70 | // Collect all the extern crates (in a reliable order). | |
71 | let mut crates_to_lint = vec![]; | |
04454e1e FG |
72 | |
73 | for id in tcx.hir().items() { | |
2b03887a | 74 | if matches!(tcx.def_kind(id.owner_id), DefKind::ExternCrate) { |
04454e1e FG |
75 | let item = tcx.hir().item(id); |
76 | if let hir::ItemKind::ExternCrate(orig_name) = item.kind { | |
77 | crates_to_lint.push(ExternCrateToLint { | |
2b03887a | 78 | def_id: item.owner_id.to_def_id(), |
04454e1e FG |
79 | span: item.span, |
80 | orig_name, | |
81 | warn_if_unused: !item.ident.as_str().starts_with('_'), | |
82 | }); | |
83 | } | |
84 | } | |
85 | } | |
94b46f34 | 86 | |
136023e0 XL |
87 | let extern_prelude = &tcx.resolutions(()).extern_prelude; |
88 | ||
94b46f34 | 89 | for extern_crate in &crates_to_lint { |
f9f354fc | 90 | let def_id = extern_crate.def_id.expect_local(); |
a2a8927a | 91 | let item = tcx.hir().expect_item(def_id); |
94b46f34 XL |
92 | |
93 | // If the crate is fully unused, we suggest removing it altogether. | |
94 | // We do this in any edition. | |
b7449926 | 95 | if extern_crate.warn_if_unused { |
f9f354fc | 96 | if let Some(&span) = unused_extern_crates.get(&def_id) { |
f2b60f7d | 97 | // Removal suggestion span needs to include attributes (Issue #54400) |
a2a8927a | 98 | let id = tcx.hir().local_def_id_to_hir_id(def_id); |
f2b60f7d FG |
99 | let span_with_attrs = tcx |
100 | .hir() | |
101 | .attrs(id) | |
102 | .iter() | |
103 | .map(|attr| attr.span) | |
104 | .fold(span, |acc, attr_span| acc.to(attr_span)); | |
105 | ||
106 | tcx.emit_spanned_lint(lint, id, span, UnusedExternCrate { span: span_with_attrs }); | |
b7449926 XL |
107 | continue; |
108 | } | |
a7813a04 | 109 | } |
94b46f34 XL |
110 | |
111 | // If we are not in Rust 2018 edition, then we don't make any further | |
112 | // suggestions. | |
113 | if !tcx.sess.rust_2018() { | |
114 | continue; | |
a7813a04 | 115 | } |
476ff2be | 116 | |
4462d4a0 | 117 | // If the extern crate isn't in the extern prelude, |
94222f64 | 118 | // there is no way it can be written as a `use`. |
0731742a | 119 | let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name); |
136023e0 | 120 | if !extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) { |
4462d4a0 XL |
121 | continue; |
122 | } | |
123 | ||
48663c56 XL |
124 | // If the extern crate is renamed, then we cannot suggest replacing it with a use as this |
125 | // would not insert the new name into the prelude, where other imports in the crate may be | |
126 | // expecting it. | |
127 | if extern_crate.orig_name.is_some() { | |
128 | continue; | |
129 | } | |
130 | ||
04454e1e | 131 | let id = tcx.hir().local_def_id_to_hir_id(def_id); |
94b46f34 XL |
132 | // If the extern crate has any attributes, they may have funky |
133 | // semantics we can't faithfully represent using `use` (most | |
134 | // notably `#[macro_use]`). Ignore it. | |
04454e1e | 135 | if !tcx.hir().attrs(id).is_empty() { |
94b46f34 XL |
136 | continue; |
137 | } | |
f2b60f7d FG |
138 | |
139 | let base_replacement = match extern_crate.orig_name { | |
140 | Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), | |
141 | None => format!("use {};", item.ident.name), | |
142 | }; | |
143 | let vis = tcx.sess.source_map().span_to_snippet(item.vis_span).unwrap_or_default(); | |
144 | let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) }; | |
145 | tcx.emit_spanned_lint( | |
146 | lint, | |
147 | id, | |
148 | extern_crate.span, | |
149 | ExternCrateNotIdiomatic { | |
150 | span: extern_crate.span, | |
151 | msg_code: add_vis("use".to_string()), | |
152 | suggestion_code: add_vis(base_replacement), | |
153 | }, | |
154 | ); | |
476ff2be | 155 | } |
a7813a04 XL |
156 | } |
157 | ||
94b46f34 | 158 | struct ExternCrateToLint { |
9fa01778 | 159 | /// `DefId` of the extern crate |
94b46f34 | 160 | def_id: DefId, |
3b2f2976 | 161 | |
94b46f34 XL |
162 | /// span from the item |
163 | span: Span, | |
164 | ||
165 | /// if `Some`, then this is renamed (`extern crate orig_name as | |
166 | /// crate_name`), and -- perhaps surprisingly -- this stores the | |
167 | /// *original* name (`item.name` will contain the new name) | |
f9f354fc | 168 | orig_name: Option<Symbol>, |
b7449926 XL |
169 | |
170 | /// if `false`, the original name started with `_`, so we shouldn't lint | |
171 | /// about it going unused (but we should still emit idiom lints). | |
172 | warn_if_unused: bool, | |
94b46f34 | 173 | } |