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