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