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.
5 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
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
;
18 use rustc_span
::symbol
::{sym, Symbol}
;
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
{
26 tcx
.hir().find(hir_id
),
30 | Node
::ForeignItem(..)
39 struct MarkSymbolVisitor
<'tcx
> {
40 worklist
: Vec
<hir
::HirId
>,
42 maybe_typeck_results
: Option
<&'tcx ty
::TypeckResults
<'tcx
>>,
43 live_symbols
: FxHashSet
<hir
::HirId
>,
44 repr_has_repr_c
: bool
,
46 inherited_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
>,
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).
58 fn typeck_results(&self) -> &'tcx ty
::TypeckResults
<'tcx
> {
59 self.maybe_typeck_results
60 .expect("`MarkSymbolVisitor::typeck_results` called outside of body")
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
);
69 self.live_symbols
.insert(hir_id
);
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
);
81 fn handle_res(&mut self, res
: Res
) {
83 Res
::Def(DefKind
::Const
| DefKind
::AssocConst
| DefKind
::TyAlias
, _
) => {
84 self.check_def_id(res
.def_id());
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
);
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
);
103 Res
::SelfTy(t
, i
) => {
105 self.check_def_id(t
);
107 if let Some((i
, _
)) = i
{
108 self.check_def_id(i
);
111 Res
::ToolMod
| Res
::NonMacroAttr(..) | Res
::Err
=> {}
113 self.check_def_id(res
.def_id());
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
);
122 bug
!("no type-dependent def for method");
126 fn handle_field_access(&mut self, lhs
: &hir
::Expr
<'_
>, hir_id
: hir
::HirId
) {
127 match self.typeck_results().expr_ty_adjusted(lhs
).kind() {
129 let index
= self.tcx
.field_index(hir_id
, self.typeck_results());
130 self.insert_def_id(def
.non_enum_variant().fields
[index
].did
);
133 _
=> span_bug
!(lhs
.span
, "named field access on non-ADT"),
137 #[allow(dead_code)] // FIXME(81658): should be used + lint reinstated after #83171 relands.
138 fn handle_assign(&mut self, expr
: &'tcx hir
::Expr
<'tcx
>) {
141 .expr_adjustments(expr
)
143 .any(|adj
| matches
!(adj
.kind
, ty
::adjustment
::Adjust
::Deref(_
)))
145 self.visit_expr(expr
);
146 } else if let hir
::ExprKind
::Field(base
, ..) = expr
.kind
{
147 // Ignore write to field
148 self.handle_assign(base
);
150 self.visit_expr(expr
);
154 fn handle_field_pattern_match(
158 pats
: &[hir
::PatField
<'_
>],
160 let variant
= match self.typeck_results().node_type(lhs
.hir_id
).kind() {
161 ty
::Adt(adt
, _
) => adt
.variant_of_res(res
),
162 _
=> span_bug
!(lhs
.span
, "non-ADT in struct pattern"),
165 if let PatKind
::Wild
= pat
.pat
.kind
{
168 let index
= self.tcx
.field_index(pat
.hir_id
, self.typeck_results());
169 self.insert_def_id(variant
.fields
[index
].did
);
173 fn mark_live_symbols(&mut self) {
174 let mut scanned
= FxHashSet
::default();
175 while let Some(id
) = self.worklist
.pop() {
176 if !scanned
.insert(id
) {
180 // in the case of tuple struct constructors we want to check the item, not the generated
181 // tuple struct constructor function
182 let id
= self.struct_constructors
.get(&id
).cloned().unwrap_or(id
);
184 if let Some(node
) = self.tcx
.hir().find(id
) {
185 self.live_symbols
.insert(id
);
186 self.visit_node(node
);
191 fn visit_node(&mut self, node
: Node
<'tcx
>) {
192 let had_repr_c
= self.repr_has_repr_c
;
193 let had_inherited_pub_visibility
= self.inherited_pub_visibility
;
194 let had_pub_visibility
= self.pub_visibility
;
195 self.repr_has_repr_c
= false;
196 self.inherited_pub_visibility
= false;
197 self.pub_visibility
= false;
199 Node
::Item(item
) => {
200 self.pub_visibility
= item
.vis
.node
.is_pub();
203 hir
::ItemKind
::Struct(..) | hir
::ItemKind
::Union(..) => {
204 let def
= self.tcx
.adt_def(item
.def_id
);
205 self.repr_has_repr_c
= def
.repr
.c();
207 intravisit
::walk_item(self, &item
);
209 hir
::ItemKind
::Enum(..) => {
210 self.inherited_pub_visibility
= self.pub_visibility
;
212 intravisit
::walk_item(self, &item
);
214 hir
::ItemKind
::ForeignMod { .. }
=> {}
216 intravisit
::walk_item(self, &item
);
220 Node
::TraitItem(trait_item
) => {
221 intravisit
::walk_trait_item(self, trait_item
);
223 Node
::ImplItem(impl_item
) => {
224 intravisit
::walk_impl_item(self, impl_item
);
226 Node
::ForeignItem(foreign_item
) => {
227 intravisit
::walk_foreign_item(self, &foreign_item
);
231 self.pub_visibility
= had_pub_visibility
;
232 self.inherited_pub_visibility
= had_inherited_pub_visibility
;
233 self.repr_has_repr_c
= had_repr_c
;
236 fn mark_as_used_if_union(&mut self, adt
: &ty
::AdtDef
, fields
: &[hir
::ExprField
<'_
>]) {
237 if adt
.is_union() && adt
.non_enum_variant().fields
.len() > 1 && adt
.did
.is_local() {
238 for field
in fields
{
239 let index
= self.tcx
.field_index(field
.hir_id
, self.typeck_results());
240 self.insert_def_id(adt
.non_enum_variant().fields
[index
].did
);
246 impl<'tcx
> Visitor
<'tcx
> for MarkSymbolVisitor
<'tcx
> {
247 type Map
= intravisit
::ErasedMap
<'tcx
>;
249 fn nested_visit_map(&mut self) -> intravisit
::NestedVisitorMap
<Self::Map
> {
250 NestedVisitorMap
::None
253 fn visit_nested_body(&mut self, body
: hir
::BodyId
) {
254 let old_maybe_typeck_results
=
255 self.maybe_typeck_results
.replace(self.tcx
.typeck_body(body
));
256 let body
= self.tcx
.hir().body(body
);
257 self.visit_body(body
);
258 self.maybe_typeck_results
= old_maybe_typeck_results
;
261 fn visit_variant_data(
263 def
: &'tcx hir
::VariantData
<'tcx
>,
265 _
: &hir
::Generics
<'_
>,
269 let has_repr_c
= self.repr_has_repr_c
;
270 let inherited_pub_visibility
= self.inherited_pub_visibility
;
271 let pub_visibility
= self.pub_visibility
;
272 let live_fields
= def
.fields().iter().filter(|f
| {
273 has_repr_c
|| (pub_visibility
&& (inherited_pub_visibility
|| f
.vis
.node
.is_pub()))
275 self.live_symbols
.extend(live_fields
.map(|f
| f
.hir_id
));
277 intravisit
::walk_struct_def(self, def
);
280 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
<'tcx
>) {
282 hir
::ExprKind
::Path(ref qpath @ hir
::QPath
::TypeRelative(..)) => {
283 let res
= self.typeck_results().qpath_res(qpath
, expr
.hir_id
);
284 self.handle_res(res
);
286 hir
::ExprKind
::MethodCall(..) => {
287 self.lookup_and_handle_method(expr
.hir_id
);
289 hir
::ExprKind
::Field(ref lhs
, ..) => {
290 self.handle_field_access(&lhs
, expr
.hir_id
);
292 hir
::ExprKind
::Struct(ref qpath
, ref fields
, _
) => {
293 let res
= self.typeck_results().qpath_res(qpath
, expr
.hir_id
);
294 self.handle_res(res
);
295 if let ty
::Adt(ref adt
, _
) = self.typeck_results().expr_ty(expr
).kind() {
296 self.mark_as_used_if_union(adt
, fields
);
302 intravisit
::walk_expr(self, expr
);
305 fn visit_arm(&mut self, arm
: &'tcx hir
::Arm
<'tcx
>) {
306 // Inside the body, ignore constructions of variants
307 // necessary for the pattern to match. Those construction sites
308 // can't be reached unless the variant is constructed elsewhere.
309 let len
= self.ignore_variant_stack
.len();
310 self.ignore_variant_stack
.extend(arm
.pat
.necessary_variants());
311 intravisit
::walk_arm(self, arm
);
312 self.ignore_variant_stack
.truncate(len
);
315 fn visit_pat(&mut self, pat
: &'tcx hir
::Pat
<'tcx
>) {
318 PatKind
::Struct(ref path
, ref fields
, _
) => {
319 let res
= self.typeck_results().qpath_res(path
, pat
.hir_id
);
320 self.handle_field_pattern_match(pat
, res
, fields
);
322 PatKind
::Path(ref qpath
) => {
323 let res
= self.typeck_results().qpath_res(qpath
, pat
.hir_id
);
324 self.handle_res(res
);
329 intravisit
::walk_pat(self, pat
);
333 fn visit_path(&mut self, path
: &'tcx hir
::Path
<'tcx
>, _
: hir
::HirId
) {
334 self.handle_res(path
.res
);
335 intravisit
::walk_path(self, path
);
338 fn visit_ty(&mut self, ty
: &'tcx hir
::Ty
<'tcx
>) {
339 if let TyKind
::OpaqueDef(item_id
, _
) = ty
.kind
{
340 let item
= self.tcx
.hir().item(item_id
);
341 intravisit
::walk_item(self, item
);
343 intravisit
::walk_ty(self, ty
);
346 fn visit_anon_const(&mut self, c
: &'tcx hir
::AnonConst
) {
347 self.live_symbols
.insert(c
.hir_id
);
348 intravisit
::walk_anon_const(self, c
);
352 fn has_allow_dead_code_or_lang_attr(tcx
: TyCtxt
<'_
>, id
: hir
::HirId
) -> bool
{
353 let attrs
= tcx
.hir().attrs(id
);
354 if tcx
.sess
.contains_name(attrs
, sym
::lang
) {
358 // Stable attribute for #[lang = "panic_impl"]
359 if tcx
.sess
.contains_name(attrs
, sym
::panic_handler
) {
363 // (To be) stable attribute for #[lang = "oom"]
364 if tcx
.sess
.contains_name(attrs
, sym
::alloc_error_handler
) {
368 let def_id
= tcx
.hir().local_def_id(id
);
369 let cg_attrs
= tcx
.codegen_fn_attrs(def_id
);
371 // #[used], #[no_mangle], #[export_name], etc also keeps the item alive
372 // forcefully, e.g., for placing it in a specific section.
373 if cg_attrs
.contains_extern_indicator() || cg_attrs
.flags
.contains(CodegenFnAttrFlags
::USED
) {
377 tcx
.lint_level_at_node(lint
::builtin
::DEAD_CODE
, id
).0 == lint
::Allow
380 // This visitor seeds items that
381 // 1) We want to explicitly consider as live:
382 // * Item annotated with #[allow(dead_code)]
383 // - This is done so that if we want to suppress warnings for a
384 // group of dead functions, we only have to annotate the "root".
385 // For example, if both `f` and `g` are dead and `f` calls `g`,
386 // then annotating `f` with `#[allow(dead_code)]` will suppress
387 // warning for both `f` and `g`.
388 // * Item annotated with #[lang=".."]
389 // - This is because lang items are always callable from elsewhere.
391 // 2) We are not sure to be live or not
392 // * Implementations of traits and trait methods
393 struct LifeSeeder
<'k
, 'tcx
> {
394 worklist
: Vec
<hir
::HirId
>,
395 krate
: &'k hir
::Crate
<'k
>,
397 // see `MarkSymbolVisitor::struct_constructors`
398 struct_constructors
: FxHashMap
<hir
::HirId
, hir
::HirId
>,
401 impl<'v
, 'k
, 'tcx
> ItemLikeVisitor
<'v
> for LifeSeeder
<'k
, 'tcx
> {
402 fn visit_item(&mut self, item
: &hir
::Item
<'_
>) {
403 let allow_dead_code
= has_allow_dead_code_or_lang_attr(self.tcx
, item
.hir_id());
405 self.worklist
.push(item
.hir_id());
408 hir
::ItemKind
::Enum(ref enum_def
, _
) => {
410 self.worklist
.extend(enum_def
.variants
.iter().map(|variant
| variant
.id
));
413 for variant
in enum_def
.variants
{
414 if let Some(ctor_hir_id
) = variant
.data
.ctor_hir_id() {
415 self.struct_constructors
.insert(ctor_hir_id
, variant
.id
);
419 hir
::ItemKind
::Impl(hir
::Impl { ref of_trait, items, .. }
) => {
420 if of_trait
.is_some() {
421 self.worklist
.push(item
.hir_id());
423 for impl_item_ref
in items
{
424 let impl_item
= self.krate
.impl_item(impl_item_ref
.id
);
425 if of_trait
.is_some()
426 || has_allow_dead_code_or_lang_attr(self.tcx
, impl_item
.hir_id())
428 self.worklist
.push(impl_item_ref
.id
.hir_id());
432 hir
::ItemKind
::Struct(ref variant_data
, _
) => {
433 if let Some(ctor_hir_id
) = variant_data
.ctor_hir_id() {
434 self.struct_constructors
.insert(ctor_hir_id
, item
.hir_id());
441 fn visit_trait_item(&mut self, trait_item
: &hir
::TraitItem
<'_
>) {
442 use hir
::TraitItemKind
::{Const, Fn}
;
443 if matches
!(trait_item
.kind
, Const(_
, Some(_
)) | Fn(_
, hir
::TraitFn
::Provided(_
)))
444 && has_allow_dead_code_or_lang_attr(self.tcx
, trait_item
.hir_id())
446 self.worklist
.push(trait_item
.hir_id());
450 fn visit_impl_item(&mut self, _item
: &hir
::ImplItem
<'_
>) {
451 // ignore: we are handling this in `visit_item` above
454 fn visit_foreign_item(&mut self, foreign_item
: &hir
::ForeignItem
<'_
>) {
455 use hir
::ForeignItemKind
::{Fn, Static}
;
456 if matches
!(foreign_item
.kind
, Static(..) | Fn(..))
457 && has_allow_dead_code_or_lang_attr(self.tcx
, foreign_item
.hir_id())
459 self.worklist
.push(foreign_item
.hir_id());
464 fn create_and_seed_worklist
<'tcx
>(
466 access_levels
: &privacy
::AccessLevels
,
467 krate
: &hir
::Crate
<'_
>,
468 ) -> (Vec
<hir
::HirId
>, FxHashMap
<hir
::HirId
, hir
::HirId
>) {
469 let worklist
= access_levels
474 if level
>= privacy
::AccessLevel
::Reachable { Some(id) }
else { None }
479 tcx
.entry_fn(()).and_then(|(def_id
, _
)| {
480 def_id
.as_local().map(|def_id
| tcx
.hir().local_def_id_to_hir_id(def_id
))
483 .collect
::<Vec
<_
>>();
485 // Seed implemented trait items
486 let mut life_seeder
=
487 LifeSeeder { worklist, krate, tcx, struct_constructors: Default::default() }
;
488 krate
.visit_all_item_likes(&mut life_seeder
);
490 (life_seeder
.worklist
, life_seeder
.struct_constructors
)
495 access_levels
: &privacy
::AccessLevels
,
496 krate
: &hir
::Crate
<'_
>,
497 ) -> FxHashSet
<hir
::HirId
> {
498 let (worklist
, struct_constructors
) = create_and_seed_worklist(tcx
, access_levels
, krate
);
499 let mut symbol_visitor
= MarkSymbolVisitor
{
502 maybe_typeck_results
: None
,
503 live_symbols
: Default
::default(),
504 repr_has_repr_c
: false,
506 inherited_pub_visibility
: false,
507 pub_visibility
: false,
508 ignore_variant_stack
: vec
![],
511 symbol_visitor
.mark_live_symbols();
512 symbol_visitor
.live_symbols
515 struct DeadVisitor
<'tcx
> {
517 live_symbols
: FxHashSet
<hir
::HirId
>,
520 impl DeadVisitor
<'tcx
> {
521 fn should_warn_about_item(&mut self, item
: &hir
::Item
<'_
>) -> bool
{
522 let should_warn
= matches
!(
524 hir
::ItemKind
::Static(..)
525 | hir
::ItemKind
::Const(..)
526 | hir
::ItemKind
::Fn(..)
527 | hir
::ItemKind
::TyAlias(..)
528 | hir
::ItemKind
::Enum(..)
529 | hir
::ItemKind
::Struct(..)
530 | hir
::ItemKind
::Union(..)
532 should_warn
&& !self.symbol_is_live(item
.hir_id())
535 fn should_warn_about_field(&mut self, field
: &hir
::FieldDef
<'_
>) -> bool
{
536 let field_type
= self.tcx
.type_of(self.tcx
.hir().local_def_id(field
.hir_id
));
537 !field
.is_positional()
538 && !self.symbol_is_live(field
.hir_id
)
539 && !field_type
.is_phantom_data()
540 && !has_allow_dead_code_or_lang_attr(self.tcx
, field
.hir_id
)
543 fn should_warn_about_variant(&mut self, variant
: &hir
::Variant
<'_
>) -> bool
{
544 !self.symbol_is_live(variant
.id
) && !has_allow_dead_code_or_lang_attr(self.tcx
, variant
.id
)
547 fn should_warn_about_foreign_item(&mut self, fi
: &hir
::ForeignItem
<'_
>) -> bool
{
548 !self.symbol_is_live(fi
.hir_id())
549 && !has_allow_dead_code_or_lang_attr(self.tcx
, fi
.hir_id())
552 // id := HIR id of an item's definition.
553 fn symbol_is_live(&mut self, id
: hir
::HirId
) -> bool
{
554 if self.live_symbols
.contains(&id
) {
557 // If it's a type whose items are live, then it's live, too.
558 // This is done to handle the case where, for example, the static
559 // method of a private type is used, but the type itself is never
561 let def_id
= self.tcx
.hir().local_def_id(id
);
562 let inherent_impls
= self.tcx
.inherent_impls(def_id
);
563 for &impl_did
in inherent_impls
.iter() {
564 for item_did
in self.tcx
.associated_item_def_ids(impl_did
) {
565 if let Some(did
) = item_did
.as_local() {
566 let item_hir_id
= self.tcx
.hir().local_def_id_to_hir_id(did
);
567 if self.live_symbols
.contains(&item_hir_id
) {
579 span
: rustc_span
::Span
,
583 if !name
.as_str().starts_with('_'
) {
584 self.tcx
.struct_span_lint_hir(lint
::builtin
::DEAD_CODE
, id
, span
, |lint
| {
585 let def_id
= self.tcx
.hir().local_def_id(id
);
586 let descr
= self.tcx
.def_kind(def_id
).descr(def_id
.to_def_id());
587 lint
.build(&format
!("{} is never {}: `{}`", descr
, participle
, name
)).emit()
593 impl Visitor
<'tcx
> for DeadVisitor
<'tcx
> {
594 type Map
= Map
<'tcx
>;
596 /// Walk nested items in place so that we don't report dead-code
597 /// on inner functions when the outer function is already getting
598 /// an error. We could do this also by checking the parents, but
599 /// this is how the code is setup and it seems harmless enough.
600 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
601 NestedVisitorMap
::All(self.tcx
.hir())
604 fn visit_item(&mut self, item
: &'tcx hir
::Item
<'tcx
>) {
605 if self.should_warn_about_item(item
) {
606 // For most items, we want to highlight its identifier
607 let span
= match item
.kind
{
608 hir
::ItemKind
::Fn(..)
609 | hir
::ItemKind
::Mod(..)
610 | hir
::ItemKind
::Enum(..)
611 | hir
::ItemKind
::Struct(..)
612 | hir
::ItemKind
::Union(..)
613 | hir
::ItemKind
::Trait(..)
614 | hir
::ItemKind
::Impl { .. }
=> {
615 // FIXME(66095): Because item.span is annotated with things
616 // like expansion data, and ident.span isn't, we use the
617 // def_span method if it's part of a macro invocation
618 // (and thus has a source_callee set).
619 // We should probably annotate ident.span with the macro
620 // context, but that's a larger change.
621 if item
.span
.source_callee().is_some() {
622 self.tcx
.sess
.source_map().guess_head_span(item
.span
)
629 let participle
= match item
.kind
{
630 hir
::ItemKind
::Struct(..) => "constructed", // Issue #52325
633 self.warn_dead_code(item
.hir_id(), span
, item
.ident
.name
, participle
);
635 // Only continue if we didn't warn
636 intravisit
::walk_item(self, item
);
642 variant
: &'tcx hir
::Variant
<'tcx
>,
643 g
: &'tcx hir
::Generics
<'tcx
>,
646 if self.should_warn_about_variant(&variant
) {
647 self.warn_dead_code(variant
.id
, variant
.span
, variant
.ident
.name
, "constructed");
649 intravisit
::walk_variant(self, variant
, g
, id
);
653 fn visit_foreign_item(&mut self, fi
: &'tcx hir
::ForeignItem
<'tcx
>) {
654 if self.should_warn_about_foreign_item(fi
) {
655 self.warn_dead_code(fi
.hir_id(), fi
.span
, fi
.ident
.name
, "used");
657 intravisit
::walk_foreign_item(self, fi
);
660 fn visit_field_def(&mut self, field
: &'tcx hir
::FieldDef
<'tcx
>) {
661 if self.should_warn_about_field(&field
) {
662 self.warn_dead_code(field
.hir_id
, field
.span
, field
.ident
.name
, "read");
664 intravisit
::walk_field_def(self, field
);
667 fn visit_impl_item(&mut self, impl_item
: &'tcx hir
::ImplItem
<'tcx
>) {
668 match impl_item
.kind
{
669 hir
::ImplItemKind
::Const(_
, body_id
) => {
670 if !self.symbol_is_live(impl_item
.hir_id()) {
674 impl_item
.ident
.name
,
678 self.visit_nested_body(body_id
)
680 hir
::ImplItemKind
::Fn(_
, body_id
) => {
681 if !self.symbol_is_live(impl_item
.hir_id()) {
682 // FIXME(66095): Because impl_item.span is annotated with things
683 // like expansion data, and ident.span isn't, we use the
684 // def_span method if it's part of a macro invocation
685 // (and thus has a source_callee set).
686 // We should probably annotate ident.span with the macro
687 // context, but that's a larger change.
688 let span
= if impl_item
.span
.source_callee().is_some() {
689 self.tcx
.sess
.source_map().guess_head_span(impl_item
.span
)
693 self.warn_dead_code(impl_item
.hir_id(), span
, impl_item
.ident
.name
, "used");
695 self.visit_nested_body(body_id
)
697 hir
::ImplItemKind
::TyAlias(..) => {}
701 // Overwrite so that we don't warn the trait item itself.
702 fn visit_trait_item(&mut self, trait_item
: &'tcx hir
::TraitItem
<'tcx
>) {
703 match trait_item
.kind
{
704 hir
::TraitItemKind
::Const(_
, Some(body_id
))
705 | hir
::TraitItemKind
::Fn(_
, hir
::TraitFn
::Provided(body_id
)) => {
706 self.visit_nested_body(body_id
)
708 hir
::TraitItemKind
::Const(_
, None
)
709 | hir
::TraitItemKind
::Fn(_
, hir
::TraitFn
::Required(_
))
710 | hir
::TraitItemKind
::Type(..) => {}
715 pub fn check_crate(tcx
: TyCtxt
<'_
>) {
716 let access_levels
= &tcx
.privacy_access_levels(());
717 let krate
= tcx
.hir().krate();
718 let live_symbols
= find_live(tcx
, access_levels
, krate
);
719 let mut visitor
= DeadVisitor { tcx, live_symbols }
;
720 intravisit
::walk_crate(&mut visitor
, krate
);