]>
Commit | Line | Data |
---|---|---|
a7813a04 XL |
1 | // Copyright 2016 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | use lint; | |
a7813a04 XL |
12 | use rustc::ty::TyCtxt; |
13 | ||
14 | use syntax::ast; | |
8faf50e0 | 15 | use syntax_pos::Span; |
a7813a04 | 16 | |
94b46f34 | 17 | use rustc::hir::def_id::{DefId, LOCAL_CRATE}; |
476ff2be | 18 | use rustc::hir::itemlikevisit::ItemLikeVisitor; |
94b46f34 | 19 | use rustc::hir::print::visibility_qualified; |
ea8adc8c | 20 | use rustc::hir; |
8bb4bdeb | 21 | use rustc::util::nodemap::DefIdSet; |
a7813a04 | 22 | |
94b46f34 XL |
23 | use rustc_data_structures::fx::FxHashMap; |
24 | ||
25 | pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { | |
26 | let mut used_trait_imports = DefIdSet(); | |
27 | for &body_id in tcx.hir.krate().bodies.keys() { | |
28 | let item_def_id = tcx.hir.body_owner_def_id(body_id); | |
29 | let imports = tcx.used_trait_imports(item_def_id); | |
30 | debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports); | |
31 | used_trait_imports.extend(imports.iter()); | |
32 | } | |
33 | ||
34 | let mut visitor = CheckVisitor { tcx, used_trait_imports }; | |
35 | tcx.hir.krate().visit_all_item_likes(&mut visitor); | |
36 | ||
37 | unused_crates_lint(tcx); | |
38 | } | |
39 | ||
40 | impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for CheckVisitor<'a, 'tcx> { | |
41 | fn visit_item(&mut self, item: &hir::Item) { | |
8faf50e0 | 42 | if item.vis.node.is_pub() || item.span.is_dummy() { |
94b46f34 XL |
43 | return; |
44 | } | |
8faf50e0 | 45 | if let hir::ItemKind::Use(ref path, _) = item.node { |
94b46f34 XL |
46 | self.check_import(item.id, path.span); |
47 | } | |
48 | } | |
49 | ||
50 | fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) { | |
51 | } | |
52 | ||
53 | fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) { | |
54 | } | |
55 | } | |
56 | ||
8bb4bdeb | 57 | struct CheckVisitor<'a, 'tcx: 'a> { |
a7813a04 | 58 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
8bb4bdeb | 59 | used_trait_imports: DefIdSet, |
a7813a04 XL |
60 | } |
61 | ||
8bb4bdeb | 62 | impl<'a, 'tcx> CheckVisitor<'a, 'tcx> { |
a7813a04 | 63 | fn check_import(&self, id: ast::NodeId, span: Span) { |
ea8adc8c XL |
64 | let def_id = self.tcx.hir.local_def_id(id); |
65 | if !self.tcx.maybe_unused_trait_import(def_id) { | |
a7813a04 XL |
66 | return; |
67 | } | |
8bb4bdeb XL |
68 | |
69 | let import_def_id = self.tcx.hir.local_def_id(id); | |
70 | if self.used_trait_imports.contains(&import_def_id) { | |
a7813a04 XL |
71 | return; |
72 | } | |
c30ab7b3 SL |
73 | |
74 | let msg = if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) { | |
75 | format!("unused import: `{}`", snippet) | |
76 | } else { | |
77 | "unused import".to_string() | |
78 | }; | |
3b2f2976 | 79 | self.tcx.lint_node(lint::builtin::UNUSED_IMPORTS, id, span, &msg); |
a7813a04 XL |
80 | } |
81 | } | |
82 | ||
94b46f34 XL |
83 | fn unused_crates_lint<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) { |
84 | let lint = lint::builtin::UNUSED_EXTERN_CRATES; | |
85 | ||
86 | // Collect first the crates that are completely unused. These we | |
87 | // can always suggest removing (no matter which edition we are | |
88 | // in). | |
89 | let unused_extern_crates: FxHashMap<DefId, Span> = | |
90 | tcx.maybe_unused_extern_crates(LOCAL_CRATE) | |
91 | .iter() | |
92 | .filter(|&&(def_id, _)| { | |
93 | // The `def_id` here actually was calculated during resolution (at least | |
94 | // at the time of this writing) and is being shipped to us via a side | |
95 | // channel of the tcx. There may have been extra expansion phases, | |
96 | // however, which ended up removing the `def_id` *after* expansion such | |
97 | // as the `ReplaceBodyWithLoop` pass (which is a bit of a hack, but hey) | |
98 | // | |
99 | // As a result we need to verify that `def_id` is indeed still valid for | |
100 | // our AST and actually present in the HIR map. If it's not there then | |
101 | // there's safely nothing to warn about, and otherwise we carry on with | |
102 | // our execution. | |
103 | // | |
104 | // Note that if we carry through to the `extern_mod_stmt_cnum` query | |
105 | // below it'll cause a panic because `def_id` is actually bogus at this | |
106 | // point in time otherwise. | |
107 | if let Some(id) = tcx.hir.as_local_node_id(def_id) { | |
108 | if tcx.hir.find(id).is_none() { | |
109 | return false; | |
110 | } | |
111 | } | |
112 | true | |
113 | }) | |
114 | .filter(|&&(def_id, _)| { | |
115 | let cnum = tcx.extern_mod_stmt_cnum(def_id).unwrap(); | |
116 | !tcx.is_compiler_builtins(cnum) | |
117 | && !tcx.is_panic_runtime(cnum) | |
118 | && !tcx.has_global_allocator(cnum) | |
119 | }) | |
120 | .cloned() | |
121 | .collect(); | |
122 | ||
123 | // Collect all the extern crates (in a reliable order). | |
124 | let mut crates_to_lint = vec![]; | |
125 | tcx.hir.krate().visit_all_item_likes(&mut CollectExternCrateVisitor { | |
126 | tcx, | |
127 | crates_to_lint: &mut crates_to_lint, | |
128 | }); | |
129 | ||
130 | for extern_crate in &crates_to_lint { | |
131 | assert!(extern_crate.def_id.is_local()); | |
132 | ||
133 | // If the crate is fully unused, we suggest removing it altogether. | |
134 | // We do this in any edition. | |
135 | if let Some(&span) = unused_extern_crates.get(&extern_crate.def_id) { | |
136 | assert_eq!(extern_crate.def_id.krate, LOCAL_CRATE); | |
137 | let hir_id = tcx.hir.definitions().def_index_to_hir_id(extern_crate.def_id.index); | |
138 | let id = tcx.hir.hir_to_node_id(hir_id); | |
139 | let msg = "unused extern crate"; | |
140 | tcx.struct_span_lint_node(lint, id, span, msg) | |
141 | .span_suggestion_short(span, "remove it", "".to_string()) | |
142 | .emit(); | |
143 | continue; | |
a7813a04 | 144 | } |
94b46f34 XL |
145 | |
146 | // If we are not in Rust 2018 edition, then we don't make any further | |
147 | // suggestions. | |
148 | if !tcx.sess.rust_2018() { | |
149 | continue; | |
a7813a04 | 150 | } |
476ff2be | 151 | |
94b46f34 XL |
152 | // If the extern crate has any attributes, they may have funky |
153 | // semantics we can't faithfully represent using `use` (most | |
154 | // notably `#[macro_use]`). Ignore it. | |
155 | if !tcx.get_attrs(extern_crate.def_id).is_empty() { | |
156 | continue; | |
157 | } | |
32a655c1 | 158 | |
94b46f34 XL |
159 | // Otherwise, we can convert it into a `use` of some kind. |
160 | let hir_id = tcx.hir.definitions().def_index_to_hir_id(extern_crate.def_id.index); | |
161 | let id = tcx.hir.hir_to_node_id(hir_id); | |
162 | let item = tcx.hir.expect_item(id); | |
163 | let msg = "`extern crate` is not idiomatic in the new edition"; | |
164 | let help = format!( | |
165 | "convert it to a `{}`", | |
166 | visibility_qualified(&item.vis, "use") | |
167 | ); | |
168 | let base_replacement = match extern_crate.orig_name { | |
169 | Some(orig_name) => format!("use {} as {};", orig_name, item.name), | |
170 | None => format!("use {};", item.name), | |
171 | }; | |
172 | let replacement = visibility_qualified(&item.vis, &base_replacement); | |
173 | tcx.struct_span_lint_node(lint, id, extern_crate.span, msg) | |
174 | .span_suggestion_short(extern_crate.span, &help, replacement) | |
175 | .emit(); | |
476ff2be | 176 | } |
a7813a04 XL |
177 | } |
178 | ||
94b46f34 XL |
179 | struct CollectExternCrateVisitor<'a, 'tcx: 'a> { |
180 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
181 | crates_to_lint: &'a mut Vec<ExternCrateToLint>, | |
182 | } | |
8bb4bdeb | 183 | |
94b46f34 XL |
184 | struct ExternCrateToLint { |
185 | /// def-id of the extern crate | |
186 | def_id: DefId, | |
3b2f2976 | 187 | |
94b46f34 XL |
188 | /// span from the item |
189 | span: Span, | |
190 | ||
191 | /// if `Some`, then this is renamed (`extern crate orig_name as | |
192 | /// crate_name`), and -- perhaps surprisingly -- this stores the | |
193 | /// *original* name (`item.name` will contain the new name) | |
194 | orig_name: Option<ast::Name>, | |
195 | } | |
196 | ||
197 | impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for CollectExternCrateVisitor<'a, 'tcx> { | |
198 | fn visit_item(&mut self, item: &hir::Item) { | |
8faf50e0 | 199 | if let hir::ItemKind::ExternCrate(orig_name) = item.node { |
94b46f34 XL |
200 | let extern_crate_def_id = self.tcx.hir.local_def_id(item.id); |
201 | self.crates_to_lint.push( | |
202 | ExternCrateToLint { | |
203 | def_id: extern_crate_def_id, | |
204 | span: item.span, | |
205 | orig_name, | |
206 | } | |
207 | ); | |
ea8adc8c | 208 | } |
94b46f34 XL |
209 | } |
210 | ||
211 | fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) { | |
212 | } | |
213 | ||
214 | fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) { | |
3b2f2976 | 215 | } |
a7813a04 | 216 | } |