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