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