]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | // Copyright 2013 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 | // This implements the dead-code warning pass. It follows middle::reachable | |
12 | // closely. The idea is that all reachable symbols are live, codes called | |
13 | // from live codes are live, and everything else is dead. | |
14 | ||
32a655c1 | 15 | use hir::map as hir_map; |
3b2f2976 | 16 | use hir::{self, Item_, PatKind}; |
476ff2be SL |
17 | use hir::intravisit::{self, Visitor, NestedVisitorMap}; |
18 | use hir::itemlikevisit::ItemLikeVisitor; | |
54a0048b | 19 | |
54a0048b | 20 | use hir::def::Def; |
cc61c64b | 21 | use hir::def_id::{DefId, LOCAL_CRATE}; |
1a4d82fc | 22 | use lint; |
3b2f2976 XL |
23 | use middle::privacy; |
24 | use ty::{self, TyCtxt}; | |
476ff2be | 25 | use util::nodemap::FxHashSet; |
1a4d82fc | 26 | |
62682a34 | 27 | use syntax::{ast, codemap}; |
a7813a04 | 28 | use syntax::attr; |
3157f602 | 29 | use syntax_pos; |
1a4d82fc JJ |
30 | |
31 | // Any local node that may call something in its body block should be | |
32 | // explored. For example, if it's a live NodeItem that is a | |
33 | // function, then we should explore its block to check for codes that | |
34 | // may need to be marked as live. | |
a7813a04 XL |
35 | fn should_explore<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
36 | node_id: ast::NodeId) -> bool { | |
32a655c1 SL |
37 | match tcx.hir.find(node_id) { |
38 | Some(hir_map::NodeItem(..)) | | |
39 | Some(hir_map::NodeImplItem(..)) | | |
40 | Some(hir_map::NodeForeignItem(..)) | | |
41 | Some(hir_map::NodeTraitItem(..)) => | |
b039eaaf SL |
42 | true, |
43 | _ => | |
44 | false | |
1a4d82fc JJ |
45 | } |
46 | } | |
47 | ||
48 | struct MarkSymbolVisitor<'a, 'tcx: 'a> { | |
49 | worklist: Vec<ast::NodeId>, | |
a7813a04 | 50 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
32a655c1 | 51 | tables: &'a ty::TypeckTables<'tcx>, |
476ff2be | 52 | live_symbols: Box<FxHashSet<ast::NodeId>>, |
2c00a5a8 | 53 | repr_has_repr_c: bool, |
abe05a73 | 54 | in_pat: bool, |
1a4d82fc | 55 | inherited_pub_visibility: bool, |
b039eaaf | 56 | ignore_variant_stack: Vec<DefId>, |
1a4d82fc JJ |
57 | } |
58 | ||
59 | impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { | |
e9174d1e | 60 | fn check_def_id(&mut self, def_id: DefId) { |
32a655c1 | 61 | if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { |
b039eaaf SL |
62 | if should_explore(self.tcx, node_id) { |
63 | self.worklist.push(node_id); | |
64 | } | |
65 | self.live_symbols.insert(node_id); | |
66 | } | |
67 | } | |
68 | ||
69 | fn insert_def_id(&mut self, def_id: DefId) { | |
32a655c1 | 70 | if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) { |
b039eaaf SL |
71 | debug_assert!(!should_explore(self.tcx, node_id)); |
72 | self.live_symbols.insert(node_id); | |
1a4d82fc | 73 | } |
1a4d82fc JJ |
74 | } |
75 | ||
476ff2be | 76 | fn handle_definition(&mut self, def: Def) { |
3157f602 | 77 | match def { |
abe05a73 | 78 | Def::Const(_) | Def::AssociatedConst(..) | Def::TyAlias(_) => { |
3157f602 XL |
79 | self.check_def_id(def.def_id()); |
80 | } | |
abe05a73 | 81 | _ if self.in_pat => (), |
ea8adc8c XL |
82 | Def::PrimTy(..) | Def::SelfTy(..) | |
83 | Def::Local(..) | Def::Upvar(..) => {} | |
c30ab7b3 | 84 | Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { |
9e0c209e SL |
85 | if let Some(enum_id) = self.tcx.parent_def_id(variant_id) { |
86 | self.check_def_id(enum_id); | |
87 | } | |
3157f602 XL |
88 | if !self.ignore_variant_stack.contains(&variant_id) { |
89 | self.check_def_id(variant_id); | |
1a4d82fc JJ |
90 | } |
91 | } | |
3157f602 XL |
92 | _ => { |
93 | self.check_def_id(def.def_id()); | |
94 | } | |
95 | } | |
1a4d82fc JJ |
96 | } |
97 | ||
3b2f2976 XL |
98 | fn lookup_and_handle_method(&mut self, id: hir::HirId) { |
99 | self.check_def_id(self.tables.type_dependent_defs()[id].def_id()); | |
1a4d82fc JJ |
100 | } |
101 | ||
e9174d1e | 102 | fn handle_field_access(&mut self, lhs: &hir::Expr, name: ast::Name) { |
32a655c1 | 103 | match self.tables.expr_ty_adjusted(lhs).sty { |
9e0c209e | 104 | ty::TyAdt(def, _) => { |
2c00a5a8 | 105 | self.insert_def_id(def.non_enum_variant().field_named(name).did); |
9e0c209e SL |
106 | } |
107 | _ => span_bug!(lhs.span, "named field access on non-ADT"), | |
1a4d82fc JJ |
108 | } |
109 | } | |
110 | ||
e9174d1e | 111 | fn handle_tup_field_access(&mut self, lhs: &hir::Expr, idx: usize) { |
32a655c1 | 112 | match self.tables.expr_ty_adjusted(lhs).sty { |
9e0c209e | 113 | ty::TyAdt(def, _) => { |
2c00a5a8 | 114 | self.insert_def_id(def.non_enum_variant().fields[idx].did); |
9e0c209e SL |
115 | } |
116 | ty::TyTuple(..) => {} | |
117 | _ => span_bug!(lhs.span, "numeric field access on non-ADT"), | |
1a4d82fc JJ |
118 | } |
119 | } | |
120 | ||
476ff2be | 121 | fn handle_field_pattern_match(&mut self, lhs: &hir::Pat, def: Def, |
e9174d1e | 122 | pats: &[codemap::Spanned<hir::FieldPat>]) { |
3b2f2976 | 123 | let variant = match self.tables.node_id_to_type(lhs.hir_id).sty { |
476ff2be | 124 | ty::TyAdt(adt, _) => adt.variant_of_def(def), |
54a0048b | 125 | _ => span_bug!(lhs.span, "non-ADT in struct pattern") |
1a4d82fc | 126 | }; |
85aaf69f | 127 | for pat in pats { |
7453a54e | 128 | if let PatKind::Wild = pat.node.pat.node { |
62682a34 SL |
129 | continue; |
130 | } | |
b039eaaf | 131 | self.insert_def_id(variant.field_named(pat.node.name).did); |
1a4d82fc JJ |
132 | } |
133 | } | |
134 | ||
135 | fn mark_live_symbols(&mut self) { | |
476ff2be | 136 | let mut scanned = FxHashSet(); |
9346a6ac | 137 | while !self.worklist.is_empty() { |
1a4d82fc JJ |
138 | let id = self.worklist.pop().unwrap(); |
139 | if scanned.contains(&id) { | |
140 | continue | |
141 | } | |
142 | scanned.insert(id); | |
143 | ||
32a655c1 | 144 | if let Some(ref node) = self.tcx.hir.find(id) { |
3157f602 XL |
145 | self.live_symbols.insert(id); |
146 | self.visit_node(node); | |
1a4d82fc JJ |
147 | } |
148 | } | |
149 | } | |
150 | ||
32a655c1 | 151 | fn visit_node(&mut self, node: &hir_map::Node<'tcx>) { |
2c00a5a8 XL |
152 | let had_repr_c = self.repr_has_repr_c; |
153 | self.repr_has_repr_c = false; | |
1a4d82fc JJ |
154 | let had_inherited_pub_visibility = self.inherited_pub_visibility; |
155 | self.inherited_pub_visibility = false; | |
156 | match *node { | |
32a655c1 | 157 | hir_map::NodeItem(item) => { |
1a4d82fc | 158 | match item.node { |
9e0c209e | 159 | hir::ItemStruct(..) | hir::ItemUnion(..) => { |
8bb4bdeb | 160 | let def_id = self.tcx.hir.local_def_id(item.id); |
7cac9316 | 161 | let def = self.tcx.adt_def(def_id); |
2c00a5a8 | 162 | self.repr_has_repr_c = def.repr.c(); |
1a4d82fc | 163 | |
7453a54e | 164 | intravisit::walk_item(self, &item); |
1a4d82fc | 165 | } |
e9174d1e SL |
166 | hir::ItemEnum(..) => { |
167 | self.inherited_pub_visibility = item.vis == hir::Public; | |
7453a54e | 168 | intravisit::walk_item(self, &item); |
1a4d82fc | 169 | } |
e9174d1e SL |
170 | hir::ItemFn(..) |
171 | | hir::ItemTy(..) | |
172 | | hir::ItemStatic(..) | |
173 | | hir::ItemConst(..) => { | |
7453a54e | 174 | intravisit::walk_item(self, &item); |
1a4d82fc JJ |
175 | } |
176 | _ => () | |
177 | } | |
178 | } | |
32a655c1 | 179 | hir_map::NodeTraitItem(trait_item) => { |
92a42be0 | 180 | intravisit::walk_trait_item(self, trait_item); |
1a4d82fc | 181 | } |
32a655c1 | 182 | hir_map::NodeImplItem(impl_item) => { |
92a42be0 | 183 | intravisit::walk_impl_item(self, impl_item); |
1a4d82fc | 184 | } |
32a655c1 | 185 | hir_map::NodeForeignItem(foreign_item) => { |
7453a54e | 186 | intravisit::walk_foreign_item(self, &foreign_item); |
1a4d82fc JJ |
187 | } |
188 | _ => () | |
189 | } | |
2c00a5a8 | 190 | self.repr_has_repr_c = had_repr_c; |
1a4d82fc JJ |
191 | self.inherited_pub_visibility = had_inherited_pub_visibility; |
192 | } | |
3b2f2976 XL |
193 | |
194 | fn mark_as_used_if_union(&mut self, did: DefId, fields: &hir::HirVec<hir::Field>) { | |
195 | if let Some(node_id) = self.tcx.hir.as_local_node_id(did) { | |
196 | if let Some(hir_map::NodeItem(item)) = self.tcx.hir.find(node_id) { | |
197 | if let Item_::ItemUnion(ref variant, _) = item.node { | |
198 | if variant.fields().len() > 1 { | |
199 | for field in variant.fields() { | |
200 | if fields.iter().find(|x| x.name.node == field.name).is_some() { | |
201 | self.live_symbols.insert(field.id); | |
202 | } | |
203 | } | |
204 | } | |
205 | } | |
206 | } | |
207 | } | |
208 | } | |
1a4d82fc JJ |
209 | } |
210 | ||
476ff2be SL |
211 | impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> { |
212 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { | |
32a655c1 SL |
213 | NestedVisitorMap::None |
214 | } | |
215 | ||
216 | fn visit_nested_body(&mut self, body: hir::BodyId) { | |
217 | let old_tables = self.tables; | |
218 | self.tables = self.tcx.body_tables(body); | |
219 | let body = self.tcx.hir.body(body); | |
220 | self.visit_body(body); | |
221 | self.tables = old_tables; | |
476ff2be | 222 | } |
1a4d82fc | 223 | |
476ff2be | 224 | fn visit_variant_data(&mut self, def: &'tcx hir::VariantData, _: ast::Name, |
3157f602 | 225 | _: &hir::Generics, _: ast::NodeId, _: syntax_pos::Span) { |
2c00a5a8 | 226 | let has_repr_c = self.repr_has_repr_c; |
1a4d82fc | 227 | let inherited_pub_visibility = self.inherited_pub_visibility; |
b039eaaf | 228 | let live_fields = def.fields().iter().filter(|f| { |
2c00a5a8 | 229 | has_repr_c || inherited_pub_visibility || f.vis == hir::Public |
1a4d82fc | 230 | }); |
54a0048b | 231 | self.live_symbols.extend(live_fields.map(|f| f.id)); |
1a4d82fc | 232 | |
92a42be0 | 233 | intravisit::walk_struct_def(self, def); |
1a4d82fc JJ |
234 | } |
235 | ||
476ff2be | 236 | fn visit_expr(&mut self, expr: &'tcx hir::Expr) { |
1a4d82fc | 237 | match expr.node { |
476ff2be | 238 | hir::ExprPath(ref qpath @ hir::QPath::TypeRelative(..)) => { |
3b2f2976 | 239 | let def = self.tables.qpath_def(qpath, expr.hir_id); |
476ff2be SL |
240 | self.handle_definition(def); |
241 | } | |
e9174d1e | 242 | hir::ExprMethodCall(..) => { |
3b2f2976 | 243 | self.lookup_and_handle_method(expr.hir_id); |
1a4d82fc | 244 | } |
b039eaaf | 245 | hir::ExprField(ref lhs, ref name) => { |
7453a54e | 246 | self.handle_field_access(&lhs, name.node); |
1a4d82fc | 247 | } |
e9174d1e | 248 | hir::ExprTupField(ref lhs, idx) => { |
7453a54e | 249 | self.handle_tup_field_access(&lhs, idx.node); |
1a4d82fc | 250 | } |
3b2f2976 XL |
251 | hir::ExprStruct(_, ref fields, _) => { |
252 | if let ty::TypeVariants::TyAdt(ref def, _) = self.tables.expr_ty(expr).sty { | |
253 | if def.is_union() { | |
254 | self.mark_as_used_if_union(def.did, fields); | |
255 | } | |
256 | } | |
257 | } | |
1a4d82fc JJ |
258 | _ => () |
259 | } | |
260 | ||
92a42be0 | 261 | intravisit::walk_expr(self, expr); |
1a4d82fc JJ |
262 | } |
263 | ||
476ff2be | 264 | fn visit_arm(&mut self, arm: &'tcx hir::Arm) { |
62682a34 | 265 | if arm.pats.len() == 1 { |
476ff2be | 266 | let variants = arm.pats[0].necessary_variants(); |
62682a34 SL |
267 | |
268 | // Inside the body, ignore constructions of variants | |
269 | // necessary for the pattern to match. Those construction sites | |
270 | // can't be reached unless the variant is constructed elsewhere. | |
271 | let len = self.ignore_variant_stack.len(); | |
7453a54e | 272 | self.ignore_variant_stack.extend_from_slice(&variants); |
92a42be0 | 273 | intravisit::walk_arm(self, arm); |
62682a34 SL |
274 | self.ignore_variant_stack.truncate(len); |
275 | } else { | |
92a42be0 | 276 | intravisit::walk_arm(self, arm); |
62682a34 SL |
277 | } |
278 | } | |
279 | ||
476ff2be | 280 | fn visit_pat(&mut self, pat: &'tcx hir::Pat) { |
1a4d82fc | 281 | match pat.node { |
476ff2be SL |
282 | PatKind::Struct(hir::QPath::Resolved(_, ref path), ref fields, _) => { |
283 | self.handle_field_pattern_match(pat, path.def, fields); | |
1a4d82fc | 284 | } |
476ff2be | 285 | PatKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { |
3b2f2976 | 286 | let def = self.tables.qpath_def(qpath, pat.hir_id); |
476ff2be | 287 | self.handle_definition(def); |
1a4d82fc JJ |
288 | } |
289 | _ => () | |
290 | } | |
291 | ||
abe05a73 | 292 | self.in_pat = true; |
92a42be0 | 293 | intravisit::walk_pat(self, pat); |
abe05a73 | 294 | self.in_pat = false; |
1a4d82fc JJ |
295 | } |
296 | ||
476ff2be SL |
297 | fn visit_path(&mut self, path: &'tcx hir::Path, _: ast::NodeId) { |
298 | self.handle_definition(path.def); | |
92a42be0 | 299 | intravisit::walk_path(self, path); |
1a4d82fc | 300 | } |
1a4d82fc JJ |
301 | } |
302 | ||
3b2f2976 XL |
303 | fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt, |
304 | id: ast::NodeId, | |
305 | attrs: &[ast::Attribute]) -> bool { | |
85aaf69f | 306 | if attr::contains_name(attrs, "lang") { |
1a4d82fc JJ |
307 | return true; |
308 | } | |
309 | ||
7cac9316 XL |
310 | // #[used] also keeps the item alive forcefully, |
311 | // e.g. for placing it in a specific section. | |
312 | if attr::contains_name(attrs, "used") { | |
313 | return true; | |
314 | } | |
315 | ||
041b39d2 XL |
316 | // Don't lint about global allocators |
317 | if attr::contains_name(attrs, "global_allocator") { | |
318 | return true; | |
319 | } | |
320 | ||
0531ce1d XL |
321 | // These constants are special for wasm |
322 | if attr::contains_name(attrs, "wasm_custom_section") { | |
323 | return true; | |
324 | } | |
325 | ||
3b2f2976 | 326 | tcx.lint_level_at_node(lint::builtin::DEAD_CODE, id).0 == lint::Allow |
1a4d82fc JJ |
327 | } |
328 | ||
329 | // This visitor seeds items that | |
330 | // 1) We want to explicitly consider as live: | |
331 | // * Item annotated with #[allow(dead_code)] | |
332 | // - This is done so that if we want to suppress warnings for a | |
333 | // group of dead functions, we only have to annotate the "root". | |
334 | // For example, if both `f` and `g` are dead and `f` calls `g`, | |
335 | // then annotating `f` with `#[allow(dead_code)]` will suppress | |
336 | // warning for both `f` and `g`. | |
337 | // * Item annotated with #[lang=".."] | |
338 | // - This is because lang items are always callable from elsewhere. | |
339 | // or | |
340 | // 2) We are not sure to be live or not | |
341 | // * Implementation of a trait method | |
3b2f2976 | 342 | struct LifeSeeder<'k, 'tcx: 'k> { |
476ff2be SL |
343 | worklist: Vec<ast::NodeId>, |
344 | krate: &'k hir::Crate, | |
3b2f2976 | 345 | tcx: TyCtxt<'k, 'tcx, 'tcx>, |
1a4d82fc JJ |
346 | } |
347 | ||
3b2f2976 | 348 | impl<'v, 'k, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'k, 'tcx> { |
e9174d1e | 349 | fn visit_item(&mut self, item: &hir::Item) { |
3b2f2976 XL |
350 | let allow_dead_code = has_allow_dead_code_or_lang_attr(self.tcx, |
351 | item.id, | |
352 | &item.attrs); | |
1a4d82fc JJ |
353 | if allow_dead_code { |
354 | self.worklist.push(item.id); | |
355 | } | |
356 | match item.node { | |
e9174d1e | 357 | hir::ItemEnum(ref enum_def, _) if allow_dead_code => { |
b039eaaf SL |
358 | self.worklist.extend(enum_def.variants.iter() |
359 | .map(|variant| variant.node.data.id())); | |
1a4d82fc | 360 | } |
32a655c1 SL |
361 | hir::ItemTrait(.., ref trait_item_refs) => { |
362 | for trait_item_ref in trait_item_refs { | |
363 | let trait_item = self.krate.trait_item(trait_item_ref.id); | |
c34b1796 | 364 | match trait_item.node { |
32a655c1 SL |
365 | hir::TraitItemKind::Const(_, Some(_)) | |
366 | hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => { | |
3b2f2976 XL |
367 | if has_allow_dead_code_or_lang_attr(self.tcx, |
368 | trait_item.id, | |
369 | &trait_item.attrs) { | |
c34b1796 AL |
370 | self.worklist.push(trait_item.id); |
371 | } | |
372 | } | |
373 | _ => {} | |
374 | } | |
375 | } | |
376 | } | |
476ff2be SL |
377 | hir::ItemImpl(.., ref opt_trait, _, ref impl_item_refs) => { |
378 | for impl_item_ref in impl_item_refs { | |
379 | let impl_item = self.krate.impl_item(impl_item_ref.id); | |
54a0048b | 380 | if opt_trait.is_some() || |
3b2f2976 XL |
381 | has_allow_dead_code_or_lang_attr(self.tcx, |
382 | impl_item.id, | |
383 | &impl_item.attrs) { | |
476ff2be | 384 | self.worklist.push(impl_item_ref.id.node_id); |
1a4d82fc JJ |
385 | } |
386 | } | |
387 | } | |
388 | _ => () | |
389 | } | |
1a4d82fc | 390 | } |
476ff2be | 391 | |
32a655c1 SL |
392 | fn visit_trait_item(&mut self, _item: &hir::TraitItem) { |
393 | // ignore: we are handling this in `visit_item` above | |
394 | } | |
395 | ||
476ff2be SL |
396 | fn visit_impl_item(&mut self, _item: &hir::ImplItem) { |
397 | // ignore: we are handling this in `visit_item` above | |
398 | } | |
1a4d82fc JJ |
399 | } |
400 | ||
a7813a04 XL |
401 | fn create_and_seed_worklist<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
402 | access_levels: &privacy::AccessLevels, | |
403 | krate: &hir::Crate) | |
404 | -> Vec<ast::NodeId> { | |
1a4d82fc | 405 | let mut worklist = Vec::new(); |
92a42be0 | 406 | for (id, _) in &access_levels.map { |
1a4d82fc JJ |
407 | worklist.push(*id); |
408 | } | |
409 | ||
410 | // Seed entry point | |
3157f602 XL |
411 | if let Some((id, _)) = *tcx.sess.entry_fn.borrow() { |
412 | worklist.push(id); | |
1a4d82fc JJ |
413 | } |
414 | ||
d9579d0f | 415 | // Seed implemented trait items |
1a4d82fc | 416 | let mut life_seeder = LifeSeeder { |
041b39d2 XL |
417 | worklist, |
418 | krate, | |
3b2f2976 | 419 | tcx, |
1a4d82fc | 420 | }; |
476ff2be | 421 | krate.visit_all_item_likes(&mut life_seeder); |
1a4d82fc JJ |
422 | |
423 | return life_seeder.worklist; | |
424 | } | |
425 | ||
a7813a04 XL |
426 | fn find_live<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
427 | access_levels: &privacy::AccessLevels, | |
428 | krate: &hir::Crate) | |
476ff2be | 429 | -> Box<FxHashSet<ast::NodeId>> { |
92a42be0 | 430 | let worklist = create_and_seed_worklist(tcx, access_levels, krate); |
32a655c1 | 431 | let mut symbol_visitor = MarkSymbolVisitor { |
041b39d2 XL |
432 | worklist, |
433 | tcx, | |
3b2f2976 | 434 | tables: &ty::TypeckTables::empty(None), |
32a655c1 | 435 | live_symbols: box FxHashSet(), |
2c00a5a8 | 436 | repr_has_repr_c: false, |
abe05a73 | 437 | in_pat: false, |
32a655c1 SL |
438 | inherited_pub_visibility: false, |
439 | ignore_variant_stack: vec![], | |
440 | }; | |
1a4d82fc JJ |
441 | symbol_visitor.mark_live_symbols(); |
442 | symbol_visitor.live_symbols | |
443 | } | |
444 | ||
e9174d1e | 445 | fn get_struct_ctor_id(item: &hir::Item) -> Option<ast::NodeId> { |
1a4d82fc | 446 | match item.node { |
b039eaaf SL |
447 | hir::ItemStruct(ref struct_def, _) if !struct_def.is_struct() => { |
448 | Some(struct_def.id()) | |
449 | } | |
1a4d82fc JJ |
450 | _ => None |
451 | } | |
452 | } | |
453 | ||
454 | struct DeadVisitor<'a, 'tcx: 'a> { | |
a7813a04 | 455 | tcx: TyCtxt<'a, 'tcx, 'tcx>, |
476ff2be | 456 | live_symbols: Box<FxHashSet<ast::NodeId>>, |
1a4d82fc JJ |
457 | } |
458 | ||
459 | impl<'a, 'tcx> DeadVisitor<'a, 'tcx> { | |
e9174d1e | 460 | fn should_warn_about_item(&mut self, item: &hir::Item) -> bool { |
1a4d82fc | 461 | let should_warn = match item.node { |
e9174d1e SL |
462 | hir::ItemStatic(..) |
463 | | hir::ItemConst(..) | |
464 | | hir::ItemFn(..) | |
476ff2be | 465 | | hir::ItemTy(..) |
e9174d1e | 466 | | hir::ItemEnum(..) |
9e0c209e SL |
467 | | hir::ItemStruct(..) |
468 | | hir::ItemUnion(..) => true, | |
1a4d82fc JJ |
469 | _ => false |
470 | }; | |
471 | let ctor_id = get_struct_ctor_id(item); | |
472 | should_warn && !self.symbol_is_live(item.id, ctor_id) | |
473 | } | |
474 | ||
54a0048b | 475 | fn should_warn_about_field(&mut self, field: &hir::StructField) -> bool { |
7cac9316 | 476 | let field_type = self.tcx.type_of(self.tcx.hir.local_def_id(field.id)); |
c1a9b12d | 477 | let is_marker_field = match field_type.ty_to_def_id() { |
ea8adc8c | 478 | Some(def_id) => self.tcx.lang_items().items().iter().any(|item| *item == Some(def_id)), |
1a4d82fc JJ |
479 | _ => false |
480 | }; | |
54a0048b SL |
481 | !field.is_positional() |
482 | && !self.symbol_is_live(field.id, None) | |
1a4d82fc | 483 | && !is_marker_field |
3b2f2976 | 484 | && !has_allow_dead_code_or_lang_attr(self.tcx, field.id, &field.attrs) |
1a4d82fc JJ |
485 | } |
486 | ||
e9174d1e | 487 | fn should_warn_about_variant(&mut self, variant: &hir::Variant_) -> bool { |
b039eaaf | 488 | !self.symbol_is_live(variant.data.id(), None) |
3b2f2976 XL |
489 | && !has_allow_dead_code_or_lang_attr(self.tcx, |
490 | variant.data.id(), | |
491 | &variant.attrs) | |
1a4d82fc JJ |
492 | } |
493 | ||
32a655c1 SL |
494 | fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem) -> bool { |
495 | !self.symbol_is_live(fi.id, None) | |
3b2f2976 | 496 | && !has_allow_dead_code_or_lang_attr(self.tcx, fi.id, &fi.attrs) |
32a655c1 SL |
497 | } |
498 | ||
1a4d82fc JJ |
499 | // id := node id of an item's definition. |
500 | // ctor_id := `Some` if the item is a struct_ctor (tuple struct), | |
501 | // `None` otherwise. | |
502 | // If the item is a struct_ctor, then either its `id` or | |
503 | // `ctor_id` (unwrapped) is in the live_symbols set. More specifically, | |
504 | // DefMap maps the ExprPath of a struct_ctor to the node referred by | |
505 | // `ctor_id`. On the other hand, in a statement like | |
506 | // `type <ident> <generics> = <ty>;` where <ty> refers to a struct_ctor, | |
507 | // DefMap maps <ty> to `id` instead. | |
b039eaaf SL |
508 | fn symbol_is_live(&mut self, |
509 | id: ast::NodeId, | |
510 | ctor_id: Option<ast::NodeId>) | |
511 | -> bool { | |
1a4d82fc JJ |
512 | if self.live_symbols.contains(&id) |
513 | || ctor_id.map_or(false, | |
514 | |ctor| self.live_symbols.contains(&ctor)) { | |
515 | return true; | |
516 | } | |
d9579d0f | 517 | // If it's a type whose items are live, then it's live, too. |
1a4d82fc JJ |
518 | // This is done to handle the case where, for example, the static |
519 | // method of a private type is used, but the type itself is never | |
520 | // called directly. | |
7cac9316 XL |
521 | let def_id = self.tcx.hir.local_def_id(id); |
522 | let inherent_impls = self.tcx.inherent_impls(def_id); | |
523 | for &impl_did in inherent_impls.iter() { | |
524 | for &item_did in &self.tcx.associated_item_def_ids(impl_did)[..] { | |
525 | if let Some(item_node_id) = self.tcx.hir.as_local_node_id(item_did) { | |
526 | if self.live_symbols.contains(&item_node_id) { | |
527 | return true; | |
1a4d82fc JJ |
528 | } |
529 | } | |
530 | } | |
531 | } | |
532 | false | |
533 | } | |
534 | ||
535 | fn warn_dead_code(&mut self, | |
536 | id: ast::NodeId, | |
3157f602 | 537 | span: syntax_pos::Span, |
9346a6ac | 538 | name: ast::Name, |
ff7c6d11 XL |
539 | node_type: &str, |
540 | participle: &str) { | |
476ff2be | 541 | if !name.as_str().starts_with("_") { |
1a4d82fc | 542 | self.tcx |
3b2f2976 XL |
543 | .lint_node(lint::builtin::DEAD_CODE, |
544 | id, | |
545 | span, | |
ff7c6d11 XL |
546 | &format!("{} is never {}: `{}`", |
547 | node_type, participle, name)); | |
1a4d82fc JJ |
548 | } |
549 | } | |
550 | } | |
551 | ||
476ff2be | 552 | impl<'a, 'tcx> Visitor<'tcx> for DeadVisitor<'a, 'tcx> { |
92a42be0 SL |
553 | /// Walk nested items in place so that we don't report dead-code |
554 | /// on inner functions when the outer function is already getting | |
555 | /// an error. We could do this also by checking the parents, but | |
556 | /// this is how the code is setup and it seems harmless enough. | |
476ff2be | 557 | fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { |
32a655c1 | 558 | NestedVisitorMap::All(&self.tcx.hir) |
92a42be0 SL |
559 | } |
560 | ||
476ff2be | 561 | fn visit_item(&mut self, item: &'tcx hir::Item) { |
1a4d82fc | 562 | if self.should_warn_about_item(item) { |
ea8adc8c XL |
563 | // For items that have a definition with a signature followed by a |
564 | // block, point only at the signature. | |
565 | let span = match item.node { | |
566 | hir::ItemFn(..) | | |
567 | hir::ItemMod(..) | | |
568 | hir::ItemEnum(..) | | |
569 | hir::ItemStruct(..) | | |
570 | hir::ItemUnion(..) | | |
571 | hir::ItemTrait(..) | | |
ea8adc8c XL |
572 | hir::ItemImpl(..) => self.tcx.sess.codemap().def_span(item.span), |
573 | _ => item.span, | |
574 | }; | |
9346a6ac AL |
575 | self.warn_dead_code( |
576 | item.id, | |
ea8adc8c | 577 | span, |
b039eaaf | 578 | item.name, |
ff7c6d11 XL |
579 | item.node.descriptive_variant(), |
580 | "used", | |
9346a6ac | 581 | ); |
1a4d82fc | 582 | } else { |
92a42be0 SL |
583 | // Only continue if we didn't warn |
584 | intravisit::walk_item(self, item); | |
585 | } | |
586 | } | |
587 | ||
476ff2be SL |
588 | fn visit_variant(&mut self, |
589 | variant: &'tcx hir::Variant, | |
590 | g: &'tcx hir::Generics, | |
591 | id: ast::NodeId) { | |
92a42be0 | 592 | if self.should_warn_about_variant(&variant.node) { |
ff7c6d11 XL |
593 | self.warn_dead_code(variant.node.data.id(), variant.span, variant.node.name, |
594 | "variant", "constructed"); | |
92a42be0 SL |
595 | } else { |
596 | intravisit::walk_variant(self, variant, g, id); | |
1a4d82fc | 597 | } |
1a4d82fc JJ |
598 | } |
599 | ||
476ff2be | 600 | fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem) { |
32a655c1 | 601 | if self.should_warn_about_foreign_item(fi) { |
ff7c6d11 XL |
602 | self.warn_dead_code(fi.id, fi.span, fi.name, |
603 | fi.node.descriptive_variant(), "used"); | |
1a4d82fc | 604 | } |
92a42be0 | 605 | intravisit::walk_foreign_item(self, fi); |
1a4d82fc JJ |
606 | } |
607 | ||
476ff2be | 608 | fn visit_struct_field(&mut self, field: &'tcx hir::StructField) { |
54a0048b | 609 | if self.should_warn_about_field(&field) { |
ff7c6d11 | 610 | self.warn_dead_code(field.id, field.span, field.name, "field", "used"); |
1a4d82fc | 611 | } |
92a42be0 | 612 | intravisit::walk_struct_field(self, field); |
1a4d82fc JJ |
613 | } |
614 | ||
476ff2be | 615 | fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { |
d9579d0f | 616 | match impl_item.node { |
32a655c1 | 617 | hir::ImplItemKind::Const(_, body_id) => { |
d9579d0f | 618 | if !self.symbol_is_live(impl_item.id, None) { |
ea8adc8c XL |
619 | self.warn_dead_code(impl_item.id, |
620 | impl_item.span, | |
621 | impl_item.name, | |
ff7c6d11 XL |
622 | "associated const", |
623 | "used"); | |
d9579d0f | 624 | } |
32a655c1 | 625 | self.visit_nested_body(body_id) |
d9579d0f | 626 | } |
476ff2be | 627 | hir::ImplItemKind::Method(_, body_id) => { |
d9579d0f | 628 | if !self.symbol_is_live(impl_item.id, None) { |
ea8adc8c | 629 | let span = self.tcx.sess.codemap().def_span(impl_item.span); |
ff7c6d11 | 630 | self.warn_dead_code(impl_item.id, span, impl_item.name, "method", "used"); |
d9579d0f | 631 | } |
32a655c1 | 632 | self.visit_nested_body(body_id) |
d9579d0f | 633 | } |
92a42be0 | 634 | hir::ImplItemKind::Type(..) => {} |
d9579d0f AL |
635 | } |
636 | } | |
637 | ||
638 | // Overwrite so that we don't warn the trait item itself. | |
476ff2be | 639 | fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { |
d9579d0f | 640 | match trait_item.node { |
32a655c1 SL |
641 | hir::TraitItemKind::Const(_, Some(body_id)) | |
642 | hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(body_id)) => { | |
643 | self.visit_nested_body(body_id) | |
1a4d82fc | 644 | } |
32a655c1 SL |
645 | hir::TraitItemKind::Const(_, None) | |
646 | hir::TraitItemKind::Method(_, hir::TraitMethod::Required(_)) | | |
647 | hir::TraitItemKind::Type(..) => {} | |
1a4d82fc JJ |
648 | } |
649 | } | |
650 | } | |
651 | ||
cc61c64b | 652 | pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { |
7cac9316 | 653 | let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE); |
32a655c1 | 654 | let krate = tcx.hir.krate(); |
92a42be0 | 655 | let live_symbols = find_live(tcx, access_levels, krate); |
3b2f2976 XL |
656 | let mut visitor = DeadVisitor { |
657 | tcx, | |
658 | live_symbols, | |
659 | }; | |
92a42be0 | 660 | intravisit::walk_crate(&mut visitor, krate); |
1a4d82fc | 661 | } |