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