]>
Commit | Line | Data |
---|---|---|
f035d41b | 1 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
dfeec247 XL |
2 | use rustc_errors::Applicability; |
3 | use rustc_hir as hir; | |
17df50a5 | 4 | use rustc_hir::def_id::{DefId, LocalDefId}; |
dfeec247 | 5 | use rustc_hir::itemlikevisit::ItemLikeVisitor; |
ba9703b0 XL |
6 | use rustc_middle::ty::TyCtxt; |
7 | use rustc_session::lint; | |
f9f354fc | 8 | use rustc_span::{Span, Symbol}; |
94b46f34 | 9 | |
416331ca | 10 | pub fn check_crate(tcx: TyCtxt<'_>) { |
f035d41b | 11 | let mut used_trait_imports = FxHashSet::default(); |
c295e0f8 | 12 | for item_def_id in tcx.hir().body_owners() { |
f9f354fc | 13 | let imports = tcx.used_trait_imports(item_def_id); |
94b46f34 XL |
14 | debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports); |
15 | used_trait_imports.extend(imports.iter()); | |
16 | } | |
17 | ||
18 | let mut visitor = CheckVisitor { tcx, used_trait_imports }; | |
c295e0f8 | 19 | tcx.hir().visit_all_item_likes(&mut visitor); |
94b46f34 XL |
20 | |
21 | unused_crates_lint(tcx); | |
22 | } | |
23 | ||
dc9dc135 | 24 | impl ItemLikeVisitor<'v> for CheckVisitor<'tcx> { |
dfeec247 | 25 | fn visit_item(&mut self, item: &hir::Item<'_>) { |
8faf50e0 | 26 | if item.vis.node.is_pub() || item.span.is_dummy() { |
94b46f34 XL |
27 | return; |
28 | } | |
c295e0f8 | 29 | if let hir::ItemKind::Use(path, _) = item.kind { |
6a06907d | 30 | self.check_import(item.item_id(), path.span); |
94b46f34 XL |
31 | } |
32 | } | |
33 | ||
dfeec247 | 34 | fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} |
94b46f34 | 35 | |
dfeec247 | 36 | fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} |
fc512014 XL |
37 | |
38 | fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} | |
94b46f34 XL |
39 | } |
40 | ||
dc9dc135 XL |
41 | struct CheckVisitor<'tcx> { |
42 | tcx: TyCtxt<'tcx>, | |
f035d41b | 43 | used_trait_imports: FxHashSet<LocalDefId>, |
a7813a04 XL |
44 | } |
45 | ||
dc9dc135 | 46 | impl CheckVisitor<'tcx> { |
6a06907d XL |
47 | fn check_import(&self, item_id: hir::ItemId, span: Span) { |
48 | if !self.tcx.maybe_unused_trait_import(item_id.def_id) { | |
a7813a04 XL |
49 | return; |
50 | } | |
8bb4bdeb | 51 | |
6a06907d | 52 | if self.used_trait_imports.contains(&item_id.def_id) { |
a7813a04 XL |
53 | return; |
54 | } | |
c30ab7b3 | 55 | |
6a06907d XL |
56 | self.tcx.struct_span_lint_hir( |
57 | lint::builtin::UNUSED_IMPORTS, | |
58 | item_id.hir_id(), | |
59 | span, | |
60 | |lint| { | |
61 | let msg = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { | |
62 | format!("unused import: `{}`", snippet) | |
63 | } else { | |
64 | "unused import".to_owned() | |
65 | }; | |
66 | lint.build(&msg).emit(); | |
67 | }, | |
68 | ); | |
a7813a04 XL |
69 | } |
70 | } | |
71 | ||
416331ca | 72 | fn unused_crates_lint(tcx: TyCtxt<'_>) { |
94b46f34 XL |
73 | let lint = lint::builtin::UNUSED_EXTERN_CRATES; |
74 | ||
75 | // Collect first the crates that are completely unused. These we | |
76 | // can always suggest removing (no matter which edition we are | |
77 | // in). | |
f9f354fc | 78 | let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx |
17df50a5 | 79 | .maybe_unused_extern_crates(()) |
94b46f34 XL |
80 | .iter() |
81 | .filter(|&&(def_id, _)| { | |
82 | // The `def_id` here actually was calculated during resolution (at least | |
83 | // at the time of this writing) and is being shipped to us via a side | |
84 | // channel of the tcx. There may have been extra expansion phases, | |
85 | // however, which ended up removing the `def_id` *after* expansion such | |
86 | // as the `ReplaceBodyWithLoop` pass (which is a bit of a hack, but hey) | |
87 | // | |
88 | // As a result we need to verify that `def_id` is indeed still valid for | |
89 | // our AST and actually present in the HIR map. If it's not there then | |
90 | // there's safely nothing to warn about, and otherwise we carry on with | |
91 | // our execution. | |
92 | // | |
93 | // Note that if we carry through to the `extern_mod_stmt_cnum` query | |
94 | // below it'll cause a panic because `def_id` is actually bogus at this | |
95 | // point in time otherwise. | |
3dfed10e | 96 | if tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)).is_none() { |
f9f354fc | 97 | return false; |
94b46f34 XL |
98 | } |
99 | true | |
100 | }) | |
101 | .filter(|&&(def_id, _)| { | |
a1dfa0c6 | 102 | tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| { |
dfeec247 XL |
103 | !tcx.is_compiler_builtins(cnum) |
104 | && !tcx.is_panic_runtime(cnum) | |
105 | && !tcx.has_global_allocator(cnum) | |
106 | && !tcx.has_panic_handler(cnum) | |
a1dfa0c6 | 107 | }) |
94b46f34 XL |
108 | }) |
109 | .cloned() | |
110 | .collect(); | |
111 | ||
112 | // Collect all the extern crates (in a reliable order). | |
113 | let mut crates_to_lint = vec![]; | |
c295e0f8 | 114 | tcx.hir().visit_all_item_likes(&mut CollectExternCrateVisitor { |
94b46f34 XL |
115 | crates_to_lint: &mut crates_to_lint, |
116 | }); | |
117 | ||
136023e0 XL |
118 | let extern_prelude = &tcx.resolutions(()).extern_prelude; |
119 | ||
94b46f34 | 120 | for extern_crate in &crates_to_lint { |
f9f354fc | 121 | let def_id = extern_crate.def_id.expect_local(); |
3dfed10e | 122 | let id = tcx.hir().local_def_id_to_hir_id(def_id); |
dc9dc135 | 123 | let item = tcx.hir().expect_item(id); |
94b46f34 XL |
124 | |
125 | // If the crate is fully unused, we suggest removing it altogether. | |
126 | // We do this in any edition. | |
b7449926 | 127 | if extern_crate.warn_if_unused { |
f9f354fc | 128 | if let Some(&span) = unused_extern_crates.get(&def_id) { |
74b04a01 XL |
129 | tcx.struct_span_lint_hir(lint, id, span, |lint| { |
130 | // Removal suggestion span needs to include attributes (Issue #54400) | |
131 | let span_with_attrs = tcx | |
132 | .get_attrs(extern_crate.def_id) | |
133 | .iter() | |
134 | .map(|attr| attr.span) | |
135 | .fold(span, |acc, attr_span| acc.to(attr_span)); | |
136 | ||
137 | lint.build("unused extern crate") | |
138 | .span_suggestion_short( | |
139 | span_with_attrs, | |
140 | "remove it", | |
141 | String::new(), | |
142 | Applicability::MachineApplicable, | |
143 | ) | |
144 | .emit(); | |
145 | }); | |
b7449926 XL |
146 | continue; |
147 | } | |
a7813a04 | 148 | } |
94b46f34 XL |
149 | |
150 | // If we are not in Rust 2018 edition, then we don't make any further | |
151 | // suggestions. | |
152 | if !tcx.sess.rust_2018() { | |
153 | continue; | |
a7813a04 | 154 | } |
476ff2be | 155 | |
4462d4a0 | 156 | // If the extern crate isn't in the extern prelude, |
94222f64 | 157 | // there is no way it can be written as a `use`. |
0731742a | 158 | let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name); |
136023e0 | 159 | if !extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) { |
4462d4a0 XL |
160 | continue; |
161 | } | |
162 | ||
48663c56 XL |
163 | // If the extern crate is renamed, then we cannot suggest replacing it with a use as this |
164 | // would not insert the new name into the prelude, where other imports in the crate may be | |
165 | // expecting it. | |
166 | if extern_crate.orig_name.is_some() { | |
167 | continue; | |
168 | } | |
169 | ||
94b46f34 XL |
170 | // If the extern crate has any attributes, they may have funky |
171 | // semantics we can't faithfully represent using `use` (most | |
172 | // notably `#[macro_use]`). Ignore it. | |
173 | if !tcx.get_attrs(extern_crate.def_id).is_empty() { | |
174 | continue; | |
175 | } | |
74b04a01 XL |
176 | tcx.struct_span_lint_hir(lint, id, extern_crate.span, |lint| { |
177 | // Otherwise, we can convert it into a `use` of some kind. | |
178 | let base_replacement = match extern_crate.orig_name { | |
179 | Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name), | |
180 | None => format!("use {};", item.ident.name), | |
181 | }; | |
ba9703b0 XL |
182 | let vis = tcx.sess.source_map().span_to_snippet(item.vis.span).unwrap_or_default(); |
183 | let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) }; | |
184 | lint.build("`extern crate` is not idiomatic in the new edition") | |
74b04a01 XL |
185 | .span_suggestion_short( |
186 | extern_crate.span, | |
ba9703b0 XL |
187 | &format!("convert it to a `{}`", add_vis("use".to_string())), |
188 | add_vis(base_replacement), | |
74b04a01 XL |
189 | Applicability::MachineApplicable, |
190 | ) | |
191 | .emit(); | |
192 | }) | |
476ff2be | 193 | } |
a7813a04 XL |
194 | } |
195 | ||
6a06907d | 196 | struct CollectExternCrateVisitor<'a> { |
94b46f34 XL |
197 | crates_to_lint: &'a mut Vec<ExternCrateToLint>, |
198 | } | |
8bb4bdeb | 199 | |
94b46f34 | 200 | struct ExternCrateToLint { |
9fa01778 | 201 | /// `DefId` of the extern crate |
94b46f34 | 202 | def_id: DefId, |
3b2f2976 | 203 | |
94b46f34 XL |
204 | /// span from the item |
205 | span: Span, | |
206 | ||
207 | /// if `Some`, then this is renamed (`extern crate orig_name as | |
208 | /// crate_name`), and -- perhaps surprisingly -- this stores the | |
209 | /// *original* name (`item.name` will contain the new name) | |
f9f354fc | 210 | orig_name: Option<Symbol>, |
b7449926 XL |
211 | |
212 | /// if `false`, the original name started with `_`, so we shouldn't lint | |
213 | /// about it going unused (but we should still emit idiom lints). | |
214 | warn_if_unused: bool, | |
94b46f34 XL |
215 | } |
216 | ||
6a06907d | 217 | impl<'a, 'v> ItemLikeVisitor<'v> for CollectExternCrateVisitor<'a> { |
dfeec247 | 218 | fn visit_item(&mut self, item: &hir::Item<'_>) { |
e74abb32 | 219 | if let hir::ItemKind::ExternCrate(orig_name) = item.kind { |
dfeec247 | 220 | self.crates_to_lint.push(ExternCrateToLint { |
6a06907d | 221 | def_id: item.def_id.to_def_id(), |
dfeec247 XL |
222 | span: item.span, |
223 | orig_name, | |
224 | warn_if_unused: !item.ident.as_str().starts_with('_'), | |
225 | }); | |
ea8adc8c | 226 | } |
94b46f34 XL |
227 | } |
228 | ||
dfeec247 | 229 | fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {} |
94b46f34 | 230 | |
dfeec247 | 231 | fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {} |
fc512014 XL |
232 | |
233 | fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {} | |
a7813a04 | 234 | } |