]>
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 | ||
9cc50fc6 | 15 | use dep_graph::DepNode; |
54a0048b SL |
16 | use hir::map as ast_map; |
17 | use hir::{self, pat_util, PatKind}; | |
18 | use hir::intravisit::{self, Visitor}; | |
19 | ||
20 | use middle::privacy; | |
21 | use ty::{self, TyCtxt}; | |
22 | use hir::def::Def; | |
23 | use hir::def_id::{DefId}; | |
1a4d82fc | 24 | use lint; |
1a4d82fc JJ |
25 | |
26 | use std::collections::HashSet; | |
62682a34 | 27 | use syntax::{ast, codemap}; |
b039eaaf | 28 | use syntax::attr::{self, AttrMetaMethods}; |
1a4d82fc JJ |
29 | |
30 | // Any local node that may call something in its body block should be | |
31 | // explored. For example, if it's a live NodeItem that is a | |
32 | // function, then we should explore its block to check for codes that | |
33 | // may need to be marked as live. | |
54a0048b | 34 | fn should_explore(tcx: &TyCtxt, node_id: ast::NodeId) -> bool { |
b039eaaf SL |
35 | match tcx.map.find(node_id) { |
36 | Some(ast_map::NodeItem(..)) | | |
37 | Some(ast_map::NodeImplItem(..)) | | |
38 | Some(ast_map::NodeForeignItem(..)) | | |
39 | Some(ast_map::NodeTraitItem(..)) => | |
40 | true, | |
41 | _ => | |
42 | false | |
1a4d82fc JJ |
43 | } |
44 | } | |
45 | ||
46 | struct MarkSymbolVisitor<'a, 'tcx: 'a> { | |
47 | worklist: Vec<ast::NodeId>, | |
54a0048b | 48 | tcx: &'a TyCtxt<'tcx>, |
1a4d82fc JJ |
49 | live_symbols: Box<HashSet<ast::NodeId>>, |
50 | struct_has_extern_repr: bool, | |
51 | ignore_non_const_paths: bool, | |
52 | inherited_pub_visibility: bool, | |
b039eaaf | 53 | ignore_variant_stack: Vec<DefId>, |
1a4d82fc JJ |
54 | } |
55 | ||
56 | impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { | |
54a0048b | 57 | fn new(tcx: &'a TyCtxt<'tcx>, |
1a4d82fc JJ |
58 | worklist: Vec<ast::NodeId>) -> MarkSymbolVisitor<'a, 'tcx> { |
59 | MarkSymbolVisitor { | |
60 | worklist: worklist, | |
61 | tcx: tcx, | |
62 | live_symbols: box HashSet::new(), | |
63 | struct_has_extern_repr: false, | |
64 | ignore_non_const_paths: false, | |
65 | inherited_pub_visibility: false, | |
62682a34 | 66 | ignore_variant_stack: vec![], |
1a4d82fc JJ |
67 | } |
68 | } | |
69 | ||
e9174d1e | 70 | fn check_def_id(&mut self, def_id: DefId) { |
b039eaaf SL |
71 | if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) { |
72 | if should_explore(self.tcx, node_id) { | |
73 | self.worklist.push(node_id); | |
74 | } | |
75 | self.live_symbols.insert(node_id); | |
76 | } | |
77 | } | |
78 | ||
79 | fn insert_def_id(&mut self, def_id: DefId) { | |
80 | if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) { | |
81 | debug_assert!(!should_explore(self.tcx, node_id)); | |
82 | self.live_symbols.insert(node_id); | |
1a4d82fc | 83 | } |
1a4d82fc JJ |
84 | } |
85 | ||
86 | fn lookup_and_handle_definition(&mut self, id: &ast::NodeId) { | |
54a0048b | 87 | use ty::TypeVariants::{TyEnum, TyStruct}; |
e9174d1e SL |
88 | |
89 | // If `bar` is a trait item, make sure to mark Foo as alive in `Foo::bar` | |
90 | self.tcx.tables.borrow().item_substs.get(id) | |
91 | .and_then(|substs| substs.substs.self_ty()) | |
92 | .map(|ty| match ty.sty { | |
93 | TyEnum(tyid, _) | TyStruct(tyid, _) => self.check_def_id(tyid.did), | |
94 | _ => (), | |
95 | }); | |
96 | ||
1a4d82fc | 97 | self.tcx.def_map.borrow().get(id).map(|def| { |
c34b1796 | 98 | match def.full_def() { |
7453a54e | 99 | Def::Const(_) | Def::AssociatedConst(..) => { |
b039eaaf | 100 | self.check_def_id(def.def_id()); |
1a4d82fc JJ |
101 | } |
102 | _ if self.ignore_non_const_paths => (), | |
7453a54e SL |
103 | Def::PrimTy(_) => (), |
104 | Def::SelfTy(..) => (), | |
105 | Def::Variant(enum_id, variant_id) => { | |
1a4d82fc | 106 | self.check_def_id(enum_id); |
b039eaaf | 107 | if !self.ignore_variant_stack.contains(&variant_id) { |
62682a34 SL |
108 | self.check_def_id(variant_id); |
109 | } | |
1a4d82fc JJ |
110 | } |
111 | _ => { | |
112 | self.check_def_id(def.def_id()); | |
113 | } | |
114 | } | |
115 | }); | |
116 | } | |
117 | ||
c1a9b12d | 118 | fn lookup_and_handle_method(&mut self, id: ast::NodeId) { |
1a4d82fc | 119 | let method_call = ty::MethodCall::expr(id); |
c1a9b12d SL |
120 | let method = self.tcx.tables.borrow().method_map[&method_call]; |
121 | self.check_def_id(method.def_id); | |
1a4d82fc JJ |
122 | } |
123 | ||
e9174d1e SL |
124 | fn handle_field_access(&mut self, lhs: &hir::Expr, name: ast::Name) { |
125 | if let ty::TyStruct(def, _) = self.tcx.expr_ty_adjusted(lhs).sty { | |
b039eaaf | 126 | self.insert_def_id(def.struct_variant().field_named(name).did); |
e9174d1e | 127 | } else { |
54a0048b | 128 | span_bug!(lhs.span, "named field access on non-struct") |
1a4d82fc JJ |
129 | } |
130 | } | |
131 | ||
e9174d1e SL |
132 | fn handle_tup_field_access(&mut self, lhs: &hir::Expr, idx: usize) { |
133 | if let ty::TyStruct(def, _) = self.tcx.expr_ty_adjusted(lhs).sty { | |
b039eaaf | 134 | self.insert_def_id(def.struct_variant().fields[idx].did); |
1a4d82fc JJ |
135 | } |
136 | } | |
137 | ||
e9174d1e SL |
138 | fn handle_field_pattern_match(&mut self, lhs: &hir::Pat, |
139 | pats: &[codemap::Spanned<hir::FieldPat>]) { | |
140 | let def = self.tcx.def_map.borrow().get(&lhs.id).unwrap().full_def(); | |
141 | let pat_ty = self.tcx.node_id_to_type(lhs.id); | |
142 | let variant = match pat_ty.sty { | |
143 | ty::TyStruct(adt, _) | ty::TyEnum(adt, _) => adt.variant_of_def(def), | |
54a0048b | 144 | _ => span_bug!(lhs.span, "non-ADT in struct pattern") |
1a4d82fc | 145 | }; |
85aaf69f | 146 | for pat in pats { |
7453a54e | 147 | if let PatKind::Wild = pat.node.pat.node { |
62682a34 SL |
148 | continue; |
149 | } | |
b039eaaf | 150 | self.insert_def_id(variant.field_named(pat.node.name).did); |
1a4d82fc JJ |
151 | } |
152 | } | |
153 | ||
154 | fn mark_live_symbols(&mut self) { | |
155 | let mut scanned = HashSet::new(); | |
9346a6ac | 156 | while !self.worklist.is_empty() { |
1a4d82fc JJ |
157 | let id = self.worklist.pop().unwrap(); |
158 | if scanned.contains(&id) { | |
159 | continue | |
160 | } | |
161 | scanned.insert(id); | |
162 | ||
163 | match self.tcx.map.find(id) { | |
164 | Some(ref node) => { | |
165 | self.live_symbols.insert(id); | |
166 | self.visit_node(node); | |
167 | } | |
168 | None => (), | |
169 | } | |
170 | } | |
171 | } | |
172 | ||
173 | fn visit_node(&mut self, node: &ast_map::Node) { | |
174 | let had_extern_repr = self.struct_has_extern_repr; | |
175 | self.struct_has_extern_repr = false; | |
176 | let had_inherited_pub_visibility = self.inherited_pub_visibility; | |
177 | self.inherited_pub_visibility = false; | |
178 | match *node { | |
179 | ast_map::NodeItem(item) => { | |
180 | match item.node { | |
e9174d1e | 181 | hir::ItemStruct(..) => { |
1a4d82fc JJ |
182 | self.struct_has_extern_repr = item.attrs.iter().any(|attr| { |
183 | attr::find_repr_attrs(self.tcx.sess.diagnostic(), attr) | |
184 | .contains(&attr::ReprExtern) | |
185 | }); | |
186 | ||
7453a54e | 187 | intravisit::walk_item(self, &item); |
1a4d82fc | 188 | } |
e9174d1e SL |
189 | hir::ItemEnum(..) => { |
190 | self.inherited_pub_visibility = item.vis == hir::Public; | |
7453a54e | 191 | intravisit::walk_item(self, &item); |
1a4d82fc | 192 | } |
e9174d1e SL |
193 | hir::ItemFn(..) |
194 | | hir::ItemTy(..) | |
195 | | hir::ItemStatic(..) | |
196 | | hir::ItemConst(..) => { | |
7453a54e | 197 | intravisit::walk_item(self, &item); |
1a4d82fc JJ |
198 | } |
199 | _ => () | |
200 | } | |
201 | } | |
c34b1796 | 202 | ast_map::NodeTraitItem(trait_item) => { |
92a42be0 | 203 | intravisit::walk_trait_item(self, trait_item); |
1a4d82fc JJ |
204 | } |
205 | ast_map::NodeImplItem(impl_item) => { | |
92a42be0 | 206 | intravisit::walk_impl_item(self, impl_item); |
1a4d82fc JJ |
207 | } |
208 | ast_map::NodeForeignItem(foreign_item) => { | |
7453a54e | 209 | intravisit::walk_foreign_item(self, &foreign_item); |
1a4d82fc JJ |
210 | } |
211 | _ => () | |
212 | } | |
213 | self.struct_has_extern_repr = had_extern_repr; | |
214 | self.inherited_pub_visibility = had_inherited_pub_visibility; | |
215 | } | |
216 | } | |
217 | ||
218 | impl<'a, 'tcx, 'v> Visitor<'v> for MarkSymbolVisitor<'a, 'tcx> { | |
219 | ||
b039eaaf SL |
220 | fn visit_variant_data(&mut self, def: &hir::VariantData, _: ast::Name, |
221 | _: &hir::Generics, _: ast::NodeId, _: codemap::Span) { | |
1a4d82fc JJ |
222 | let has_extern_repr = self.struct_has_extern_repr; |
223 | let inherited_pub_visibility = self.inherited_pub_visibility; | |
b039eaaf | 224 | let live_fields = def.fields().iter().filter(|f| { |
54a0048b | 225 | has_extern_repr || inherited_pub_visibility || f.vis == hir::Public |
1a4d82fc | 226 | }); |
54a0048b | 227 | self.live_symbols.extend(live_fields.map(|f| f.id)); |
1a4d82fc | 228 | |
92a42be0 | 229 | intravisit::walk_struct_def(self, def); |
1a4d82fc JJ |
230 | } |
231 | ||
e9174d1e | 232 | fn visit_expr(&mut self, expr: &hir::Expr) { |
1a4d82fc | 233 | match expr.node { |
e9174d1e | 234 | hir::ExprMethodCall(..) => { |
c1a9b12d | 235 | self.lookup_and_handle_method(expr.id); |
1a4d82fc | 236 | } |
b039eaaf | 237 | hir::ExprField(ref lhs, ref name) => { |
7453a54e | 238 | self.handle_field_access(&lhs, name.node); |
1a4d82fc | 239 | } |
e9174d1e | 240 | hir::ExprTupField(ref lhs, idx) => { |
7453a54e | 241 | self.handle_tup_field_access(&lhs, idx.node); |
1a4d82fc JJ |
242 | } |
243 | _ => () | |
244 | } | |
245 | ||
92a42be0 | 246 | intravisit::walk_expr(self, expr); |
1a4d82fc JJ |
247 | } |
248 | ||
e9174d1e | 249 | fn visit_arm(&mut self, arm: &hir::Arm) { |
62682a34 SL |
250 | if arm.pats.len() == 1 { |
251 | let pat = &*arm.pats[0]; | |
92a42be0 | 252 | let variants = pat_util::necessary_variants(&self.tcx.def_map.borrow(), pat); |
62682a34 SL |
253 | |
254 | // Inside the body, ignore constructions of variants | |
255 | // necessary for the pattern to match. Those construction sites | |
256 | // can't be reached unless the variant is constructed elsewhere. | |
257 | let len = self.ignore_variant_stack.len(); | |
7453a54e | 258 | self.ignore_variant_stack.extend_from_slice(&variants); |
92a42be0 | 259 | intravisit::walk_arm(self, arm); |
62682a34 SL |
260 | self.ignore_variant_stack.truncate(len); |
261 | } else { | |
92a42be0 | 262 | intravisit::walk_arm(self, arm); |
62682a34 SL |
263 | } |
264 | } | |
265 | ||
e9174d1e | 266 | fn visit_pat(&mut self, pat: &hir::Pat) { |
1a4d82fc JJ |
267 | let def_map = &self.tcx.def_map; |
268 | match pat.node { | |
7453a54e | 269 | PatKind::Struct(_, ref fields, _) => { |
85aaf69f | 270 | self.handle_field_pattern_match(pat, fields); |
1a4d82fc | 271 | } |
92a42be0 | 272 | _ if pat_util::pat_is_const(&def_map.borrow(), pat) => { |
1a4d82fc JJ |
273 | // it might be the only use of a const |
274 | self.lookup_and_handle_definition(&pat.id) | |
275 | } | |
276 | _ => () | |
277 | } | |
278 | ||
279 | self.ignore_non_const_paths = true; | |
92a42be0 | 280 | intravisit::walk_pat(self, pat); |
1a4d82fc JJ |
281 | self.ignore_non_const_paths = false; |
282 | } | |
283 | ||
e9174d1e | 284 | fn visit_path(&mut self, path: &hir::Path, id: ast::NodeId) { |
1a4d82fc | 285 | self.lookup_and_handle_definition(&id); |
92a42be0 | 286 | intravisit::walk_path(self, path); |
1a4d82fc JJ |
287 | } |
288 | ||
b039eaaf SL |
289 | fn visit_path_list_item(&mut self, path: &hir::Path, item: &hir::PathListItem) { |
290 | self.lookup_and_handle_definition(&item.node.id()); | |
92a42be0 | 291 | intravisit::walk_path_list_item(self, path, item); |
1a4d82fc JJ |
292 | } |
293 | } | |
294 | ||
b039eaaf | 295 | fn has_allow_dead_code_or_lang_attr(attrs: &[ast::Attribute]) -> bool { |
85aaf69f | 296 | if attr::contains_name(attrs, "lang") { |
1a4d82fc JJ |
297 | return true; |
298 | } | |
299 | ||
300 | let dead_code = lint::builtin::DEAD_CODE.name_lower(); | |
b039eaaf | 301 | for attr in lint::gather_attrs(attrs) { |
1a4d82fc JJ |
302 | match attr { |
303 | Ok((ref name, lint::Allow, _)) | |
85aaf69f | 304 | if &name[..] == dead_code => return true, |
1a4d82fc JJ |
305 | _ => (), |
306 | } | |
307 | } | |
308 | false | |
309 | } | |
310 | ||
311 | // This visitor seeds items that | |
312 | // 1) We want to explicitly consider as live: | |
313 | // * Item annotated with #[allow(dead_code)] | |
314 | // - This is done so that if we want to suppress warnings for a | |
315 | // group of dead functions, we only have to annotate the "root". | |
316 | // For example, if both `f` and `g` are dead and `f` calls `g`, | |
317 | // then annotating `f` with `#[allow(dead_code)]` will suppress | |
318 | // warning for both `f` and `g`. | |
319 | // * Item annotated with #[lang=".."] | |
320 | // - This is because lang items are always callable from elsewhere. | |
321 | // or | |
322 | // 2) We are not sure to be live or not | |
323 | // * Implementation of a trait method | |
324 | struct LifeSeeder { | |
325 | worklist: Vec<ast::NodeId> | |
326 | } | |
327 | ||
328 | impl<'v> Visitor<'v> for LifeSeeder { | |
e9174d1e | 329 | fn visit_item(&mut self, item: &hir::Item) { |
85aaf69f | 330 | let allow_dead_code = has_allow_dead_code_or_lang_attr(&item.attrs); |
1a4d82fc JJ |
331 | if allow_dead_code { |
332 | self.worklist.push(item.id); | |
333 | } | |
334 | match item.node { | |
e9174d1e | 335 | hir::ItemEnum(ref enum_def, _) if allow_dead_code => { |
b039eaaf SL |
336 | self.worklist.extend(enum_def.variants.iter() |
337 | .map(|variant| variant.node.data.id())); | |
1a4d82fc | 338 | } |
e9174d1e | 339 | hir::ItemTrait(_, _, _, ref trait_items) => { |
c34b1796 AL |
340 | for trait_item in trait_items { |
341 | match trait_item.node { | |
e9174d1e SL |
342 | hir::ConstTraitItem(_, Some(_)) | |
343 | hir::MethodTraitItem(_, Some(_)) => { | |
c34b1796 AL |
344 | if has_allow_dead_code_or_lang_attr(&trait_item.attrs) { |
345 | self.worklist.push(trait_item.id); | |
346 | } | |
347 | } | |
348 | _ => {} | |
349 | } | |
350 | } | |
351 | } | |
e9174d1e | 352 | hir::ItemImpl(_, _, _, ref opt_trait, _, ref impl_items) => { |
85aaf69f | 353 | for impl_item in impl_items { |
54a0048b SL |
354 | if opt_trait.is_some() || |
355 | has_allow_dead_code_or_lang_attr(&impl_item.attrs) { | |
356 | self.worklist.push(impl_item.id); | |
1a4d82fc JJ |
357 | } |
358 | } | |
359 | } | |
360 | _ => () | |
361 | } | |
1a4d82fc | 362 | } |
1a4d82fc JJ |
363 | } |
364 | ||
54a0048b | 365 | fn create_and_seed_worklist(tcx: &TyCtxt, |
92a42be0 | 366 | access_levels: &privacy::AccessLevels, |
e9174d1e | 367 | krate: &hir::Crate) -> Vec<ast::NodeId> { |
1a4d82fc | 368 | let mut worklist = Vec::new(); |
92a42be0 | 369 | for (id, _) in &access_levels.map { |
1a4d82fc JJ |
370 | worklist.push(*id); |
371 | } | |
372 | ||
373 | // Seed entry point | |
374 | match *tcx.sess.entry_fn.borrow() { | |
375 | Some((id, _)) => worklist.push(id), | |
376 | None => () | |
377 | } | |
378 | ||
d9579d0f | 379 | // Seed implemented trait items |
1a4d82fc JJ |
380 | let mut life_seeder = LifeSeeder { |
381 | worklist: worklist | |
382 | }; | |
92a42be0 | 383 | krate.visit_all_items(&mut life_seeder); |
1a4d82fc JJ |
384 | |
385 | return life_seeder.worklist; | |
386 | } | |
387 | ||
54a0048b | 388 | fn find_live(tcx: &TyCtxt, |
92a42be0 | 389 | access_levels: &privacy::AccessLevels, |
e9174d1e | 390 | krate: &hir::Crate) |
1a4d82fc | 391 | -> Box<HashSet<ast::NodeId>> { |
92a42be0 | 392 | let worklist = create_and_seed_worklist(tcx, access_levels, krate); |
1a4d82fc JJ |
393 | let mut symbol_visitor = MarkSymbolVisitor::new(tcx, worklist); |
394 | symbol_visitor.mark_live_symbols(); | |
395 | symbol_visitor.live_symbols | |
396 | } | |
397 | ||
e9174d1e | 398 | fn get_struct_ctor_id(item: &hir::Item) -> Option<ast::NodeId> { |
1a4d82fc | 399 | match item.node { |
b039eaaf SL |
400 | hir::ItemStruct(ref struct_def, _) if !struct_def.is_struct() => { |
401 | Some(struct_def.id()) | |
402 | } | |
1a4d82fc JJ |
403 | _ => None |
404 | } | |
405 | } | |
406 | ||
407 | struct DeadVisitor<'a, 'tcx: 'a> { | |
54a0048b | 408 | tcx: &'a TyCtxt<'tcx>, |
1a4d82fc JJ |
409 | live_symbols: Box<HashSet<ast::NodeId>>, |
410 | } | |
411 | ||
412 | impl<'a, 'tcx> DeadVisitor<'a, 'tcx> { | |
e9174d1e | 413 | fn should_warn_about_item(&mut self, item: &hir::Item) -> bool { |
1a4d82fc | 414 | let should_warn = match item.node { |
e9174d1e SL |
415 | hir::ItemStatic(..) |
416 | | hir::ItemConst(..) | |
417 | | hir::ItemFn(..) | |
418 | | hir::ItemEnum(..) | |
419 | | hir::ItemStruct(..) => true, | |
1a4d82fc JJ |
420 | _ => false |
421 | }; | |
422 | let ctor_id = get_struct_ctor_id(item); | |
423 | should_warn && !self.symbol_is_live(item.id, ctor_id) | |
424 | } | |
425 | ||
54a0048b SL |
426 | fn should_warn_about_field(&mut self, field: &hir::StructField) -> bool { |
427 | let field_type = self.tcx.node_id_to_type(field.id); | |
c1a9b12d | 428 | let is_marker_field = match field_type.ty_to_def_id() { |
7453a54e | 429 | Some(def_id) => self.tcx.lang_items.items().iter().any(|item| *item == Some(def_id)), |
1a4d82fc JJ |
430 | _ => false |
431 | }; | |
54a0048b SL |
432 | !field.is_positional() |
433 | && !self.symbol_is_live(field.id, None) | |
1a4d82fc | 434 | && !is_marker_field |
54a0048b | 435 | && !has_allow_dead_code_or_lang_attr(&field.attrs) |
1a4d82fc JJ |
436 | } |
437 | ||
e9174d1e | 438 | fn should_warn_about_variant(&mut self, variant: &hir::Variant_) -> bool { |
b039eaaf | 439 | !self.symbol_is_live(variant.data.id(), None) |
85aaf69f | 440 | && !has_allow_dead_code_or_lang_attr(&variant.attrs) |
1a4d82fc JJ |
441 | } |
442 | ||
443 | // id := node id of an item's definition. | |
444 | // ctor_id := `Some` if the item is a struct_ctor (tuple struct), | |
445 | // `None` otherwise. | |
446 | // If the item is a struct_ctor, then either its `id` or | |
447 | // `ctor_id` (unwrapped) is in the live_symbols set. More specifically, | |
448 | // DefMap maps the ExprPath of a struct_ctor to the node referred by | |
449 | // `ctor_id`. On the other hand, in a statement like | |
450 | // `type <ident> <generics> = <ty>;` where <ty> refers to a struct_ctor, | |
451 | // DefMap maps <ty> to `id` instead. | |
b039eaaf SL |
452 | fn symbol_is_live(&mut self, |
453 | id: ast::NodeId, | |
454 | ctor_id: Option<ast::NodeId>) | |
455 | -> bool { | |
1a4d82fc JJ |
456 | if self.live_symbols.contains(&id) |
457 | || ctor_id.map_or(false, | |
458 | |ctor| self.live_symbols.contains(&ctor)) { | |
459 | return true; | |
460 | } | |
d9579d0f | 461 | // If it's a type whose items are live, then it's live, too. |
1a4d82fc JJ |
462 | // This is done to handle the case where, for example, the static |
463 | // method of a private type is used, but the type itself is never | |
464 | // called directly. | |
465 | let impl_items = self.tcx.impl_items.borrow(); | |
b039eaaf | 466 | match self.tcx.inherent_impls.borrow().get(&self.tcx.map.local_def_id(id)) { |
1a4d82fc JJ |
467 | None => (), |
468 | Some(impl_list) => { | |
62682a34 SL |
469 | for impl_did in impl_list.iter() { |
470 | for item_did in impl_items.get(impl_did).unwrap().iter() { | |
b039eaaf SL |
471 | if let Some(item_node_id) = |
472 | self.tcx.map.as_local_node_id(item_did.def_id()) { | |
473 | if self.live_symbols.contains(&item_node_id) { | |
474 | return true; | |
475 | } | |
1a4d82fc JJ |
476 | } |
477 | } | |
478 | } | |
479 | } | |
480 | } | |
481 | false | |
482 | } | |
483 | ||
484 | fn warn_dead_code(&mut self, | |
485 | id: ast::NodeId, | |
486 | span: codemap::Span, | |
9346a6ac | 487 | name: ast::Name, |
1a4d82fc | 488 | node_type: &str) { |
9346a6ac | 489 | let name = name.as_str(); |
1a4d82fc JJ |
490 | if !name.starts_with("_") { |
491 | self.tcx | |
492 | .sess | |
493 | .add_lint(lint::builtin::DEAD_CODE, | |
494 | id, | |
495 | span, | |
496 | format!("{} is never used: `{}`", node_type, name)); | |
497 | } | |
498 | } | |
499 | } | |
500 | ||
501 | impl<'a, 'tcx, 'v> Visitor<'v> for DeadVisitor<'a, 'tcx> { | |
92a42be0 SL |
502 | /// Walk nested items in place so that we don't report dead-code |
503 | /// on inner functions when the outer function is already getting | |
504 | /// an error. We could do this also by checking the parents, but | |
505 | /// this is how the code is setup and it seems harmless enough. | |
506 | fn visit_nested_item(&mut self, item: hir::ItemId) { | |
507 | self.visit_item(self.tcx.map.expect_item(item.id)) | |
508 | } | |
509 | ||
e9174d1e | 510 | fn visit_item(&mut self, item: &hir::Item) { |
1a4d82fc | 511 | if self.should_warn_about_item(item) { |
9346a6ac AL |
512 | self.warn_dead_code( |
513 | item.id, | |
514 | item.span, | |
b039eaaf | 515 | item.name, |
9346a6ac AL |
516 | item.node.descriptive_variant() |
517 | ); | |
1a4d82fc | 518 | } else { |
92a42be0 SL |
519 | // Only continue if we didn't warn |
520 | intravisit::walk_item(self, item); | |
521 | } | |
522 | } | |
523 | ||
524 | fn visit_variant(&mut self, variant: &hir::Variant, g: &hir::Generics, id: ast::NodeId) { | |
525 | if self.should_warn_about_variant(&variant.node) { | |
526 | self.warn_dead_code(variant.node.data.id(), variant.span, | |
527 | variant.node.name, "variant"); | |
528 | } else { | |
529 | intravisit::walk_variant(self, variant, g, id); | |
1a4d82fc | 530 | } |
1a4d82fc JJ |
531 | } |
532 | ||
e9174d1e | 533 | fn visit_foreign_item(&mut self, fi: &hir::ForeignItem) { |
1a4d82fc | 534 | if !self.symbol_is_live(fi.id, None) { |
b039eaaf | 535 | self.warn_dead_code(fi.id, fi.span, fi.name, fi.node.descriptive_variant()); |
1a4d82fc | 536 | } |
92a42be0 | 537 | intravisit::walk_foreign_item(self, fi); |
1a4d82fc JJ |
538 | } |
539 | ||
e9174d1e | 540 | fn visit_struct_field(&mut self, field: &hir::StructField) { |
54a0048b SL |
541 | if self.should_warn_about_field(&field) { |
542 | self.warn_dead_code(field.id, field.span, | |
543 | field.name, "struct field"); | |
1a4d82fc JJ |
544 | } |
545 | ||
92a42be0 | 546 | intravisit::walk_struct_field(self, field); |
1a4d82fc JJ |
547 | } |
548 | ||
e9174d1e | 549 | fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) { |
d9579d0f | 550 | match impl_item.node { |
92a42be0 | 551 | hir::ImplItemKind::Const(_, ref expr) => { |
d9579d0f AL |
552 | if !self.symbol_is_live(impl_item.id, None) { |
553 | self.warn_dead_code(impl_item.id, impl_item.span, | |
b039eaaf | 554 | impl_item.name, "associated const"); |
d9579d0f | 555 | } |
92a42be0 | 556 | intravisit::walk_expr(self, expr) |
d9579d0f | 557 | } |
92a42be0 | 558 | hir::ImplItemKind::Method(_, ref body) => { |
d9579d0f AL |
559 | if !self.symbol_is_live(impl_item.id, None) { |
560 | self.warn_dead_code(impl_item.id, impl_item.span, | |
b039eaaf | 561 | impl_item.name, "method"); |
d9579d0f | 562 | } |
92a42be0 | 563 | intravisit::walk_block(self, body) |
d9579d0f | 564 | } |
92a42be0 | 565 | hir::ImplItemKind::Type(..) => {} |
d9579d0f AL |
566 | } |
567 | } | |
568 | ||
569 | // Overwrite so that we don't warn the trait item itself. | |
e9174d1e | 570 | fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) { |
d9579d0f | 571 | match trait_item.node { |
e9174d1e | 572 | hir::ConstTraitItem(_, Some(ref expr)) => { |
92a42be0 | 573 | intravisit::walk_expr(self, expr) |
d9579d0f | 574 | } |
e9174d1e | 575 | hir::MethodTraitItem(_, Some(ref body)) => { |
92a42be0 | 576 | intravisit::walk_block(self, body) |
1a4d82fc | 577 | } |
e9174d1e SL |
578 | hir::ConstTraitItem(_, None) | |
579 | hir::MethodTraitItem(_, None) | | |
580 | hir::TypeTraitItem(..) => {} | |
1a4d82fc JJ |
581 | } |
582 | } | |
583 | } | |
584 | ||
54a0048b | 585 | pub fn check_crate(tcx: &TyCtxt, access_levels: &privacy::AccessLevels) { |
9cc50fc6 | 586 | let _task = tcx.dep_graph.in_task(DepNode::DeadCheck); |
1a4d82fc | 587 | let krate = tcx.map.krate(); |
92a42be0 | 588 | let live_symbols = find_live(tcx, access_levels, krate); |
1a4d82fc | 589 | let mut visitor = DeadVisitor { tcx: tcx, live_symbols: live_symbols }; |
92a42be0 | 590 | intravisit::walk_crate(&mut visitor, krate); |
1a4d82fc | 591 | } |