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, 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
;
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
,
47 ignore_variant_stack
: Vec
<DefId
>,
48 // maps from tuple struct constructors to tuple struct items
49 struct_constructors
: FxHashMap
<hir
::HirId
, hir
::HirId
>,
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).
57 fn typeck_results(&self) -> &'tcx ty
::TypeckResults
<'tcx
> {
58 self.maybe_typeck_results
59 .expect("`MarkSymbolVisitor::typeck_results` called outside of body")
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
);
68 self.live_symbols
.insert(hir_id
);
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
);
80 fn handle_res(&mut self, res
: Res
) {
82 Res
::Def(DefKind
::Const
| DefKind
::AssocConst
| DefKind
::TyAlias
, _
) => {
83 self.check_def_id(res
.def_id());
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
);
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
);
102 Res
::SelfTy(t
, i
) => {
104 self.check_def_id(t
);
106 if let Some((i
, _
)) = i
{
107 self.check_def_id(i
);
110 Res
::ToolMod
| Res
::NonMacroAttr(..) | Res
::Err
=> {}
112 self.check_def_id(res
.def_id());
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
);
121 bug
!("no type-dependent def for method");
125 fn handle_field_access(&mut self, lhs
: &hir
::Expr
<'_
>, hir_id
: hir
::HirId
) {
126 match self.typeck_results().expr_ty_adjusted(lhs
).kind() {
128 let index
= self.tcx
.field_index(hir_id
, self.typeck_results());
129 self.insert_def_id(def
.non_enum_variant().fields
[index
].did
);
132 _
=> span_bug
!(lhs
.span
, "named field access on non-ADT"),
136 fn handle_assign(&mut self, expr
: &'tcx hir
::Expr
<'tcx
>) {
139 .expr_adjustments(expr
)
141 .any(|adj
| matches
!(adj
.kind
, ty
::adjustment
::Adjust
::Deref(_
)))
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
);
148 self.visit_expr(expr
);
152 fn handle_field_pattern_match(
156 pats
: &[hir
::PatField
<'_
>],
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"),
163 if let PatKind
::Wild
= pat
.pat
.kind
{
166 let index
= self.tcx
.field_index(pat
.hir_id
, self.typeck_results());
167 self.insert_def_id(variant
.fields
[index
].did
);
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
) {
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
);
182 if let Some(node
) = self.tcx
.hir().find(id
) {
183 self.live_symbols
.insert(id
);
184 self.visit_node(node
);
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;
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();
200 intravisit
::walk_item(self, &item
);
202 hir
::ItemKind
::Enum(..) => {
203 self.inherited_pub_visibility
= item
.vis
.node
.is_pub();
205 intravisit
::walk_item(self, &item
);
207 hir
::ItemKind
::ForeignMod { .. }
=> {}
209 intravisit
::walk_item(self, &item
);
212 Node
::TraitItem(trait_item
) => {
213 intravisit
::walk_trait_item(self, trait_item
);
215 Node
::ImplItem(impl_item
) => {
216 intravisit
::walk_impl_item(self, impl_item
);
218 Node
::ForeignItem(foreign_item
) => {
219 intravisit
::walk_foreign_item(self, &foreign_item
);
223 self.repr_has_repr_c
= had_repr_c
;
224 self.inherited_pub_visibility
= had_inherited_pub_visibility
;
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
);
237 impl<'tcx
> Visitor
<'tcx
> for MarkSymbolVisitor
<'tcx
> {
238 type Map
= intravisit
::ErasedMap
<'tcx
>;
240 fn nested_visit_map(&mut self) -> intravisit
::NestedVisitorMap
<Self::Map
> {
241 NestedVisitorMap
::None
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
;
252 fn visit_variant_data(
254 def
: &'tcx hir
::VariantData
<'tcx
>,
256 _
: &hir
::Generics
<'_
>,
260 let has_repr_c
= self.repr_has_repr_c
;
261 let inherited_pub_visibility
= self.inherited_pub_visibility
;
262 let live_fields
= def
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
));
268 intravisit
::walk_struct_def(self, def
);
271 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
<'tcx
>) {
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
);
277 hir
::ExprKind
::MethodCall(..) => {
278 self.lookup_and_handle_method(expr
.hir_id
);
280 hir
::ExprKind
::Assign(ref left
, ref right
, ..) => {
281 self.handle_assign(left
);
282 self.visit_expr(right
);
285 hir
::ExprKind
::Field(ref lhs
, ..) => {
286 self.handle_field_access(&lhs
, expr
.hir_id
);
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
);
298 intravisit
::walk_expr(self, expr
);
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
);
311 fn visit_pat(&mut self, pat
: &'tcx hir
::Pat
<'tcx
>) {
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
);
318 PatKind
::Path(ref qpath
) => {
319 let res
= self.typeck_results().qpath_res(qpath
, pat
.hir_id
);
320 self.handle_res(res
);
325 intravisit
::walk_pat(self, pat
);
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
);
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
);
339 intravisit
::walk_ty(self, ty
);
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
);
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
) {
354 // Stable attribute for #[lang = "panic_impl"]
355 if tcx
.sess
.contains_name(attrs
, sym
::panic_handler
) {
359 // (To be) stable attribute for #[lang = "oom"]
360 if tcx
.sess
.contains_name(attrs
, sym
::alloc_error_handler
) {
364 let def_id
= tcx
.hir().local_def_id(id
);
365 let cg_attrs
= tcx
.codegen_fn_attrs(def_id
);
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
) {
373 tcx
.lint_level_at_node(lint
::builtin
::DEAD_CODE
, id
).0 == lint
::Allow
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.
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
>,
393 // see `MarkSymbolVisitor::struct_constructors`
394 struct_constructors
: FxHashMap
<hir
::HirId
, hir
::HirId
>,
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());
401 self.worklist
.push(item
.hir_id());
404 hir
::ItemKind
::Enum(ref enum_def
, _
) => {
406 self.worklist
.extend(enum_def
.variants
.iter().map(|variant
| variant
.id
));
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
);
415 hir
::ItemKind
::Impl(hir
::Impl { ref of_trait, items, .. }
) => {
416 if of_trait
.is_some() {
417 self.worklist
.push(item
.hir_id());
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())
424 self.worklist
.push(impl_item_ref
.id
.hir_id());
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());
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())
442 self.worklist
.push(trait_item
.hir_id());
446 fn visit_impl_item(&mut self, _item
: &hir
::ImplItem
<'_
>) {
447 // ignore: we are handling this in `visit_item` above
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())
455 self.worklist
.push(foreign_item
.hir_id());
460 fn create_and_seed_worklist
<'tcx
>(
462 access_levels
: &privacy
::AccessLevels
,
463 krate
: &hir
::Crate
<'_
>,
464 ) -> (Vec
<hir
::HirId
>, FxHashMap
<hir
::HirId
, hir
::HirId
>) {
465 let worklist
= access_levels
470 if level
>= privacy
::AccessLevel
::Reachable { Some(id) }
else { None }
475 tcx
.entry_fn(LOCAL_CRATE
).map(|(def_id
, _
)| tcx
.hir().local_def_id_to_hir_id(def_id
)),
477 .collect
::<Vec
<_
>>();
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
);
484 (life_seeder
.worklist
, life_seeder
.struct_constructors
)
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
{
496 maybe_typeck_results
: None
,
497 live_symbols
: Default
::default(),
498 repr_has_repr_c
: false,
500 inherited_pub_visibility
: false,
501 ignore_variant_stack
: vec
![],
504 symbol_visitor
.mark_live_symbols();
505 symbol_visitor
.live_symbols
508 struct DeadVisitor
<'tcx
> {
510 live_symbols
: FxHashSet
<hir
::HirId
>,
513 impl DeadVisitor
<'tcx
> {
514 fn should_warn_about_item(&mut self, item
: &hir
::Item
<'_
>) -> bool
{
515 let should_warn
= matches
!(
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(..)
525 should_warn
&& !self.symbol_is_live(item
.hir_id())
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
)
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
)
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())
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
) {
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
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
) {
572 span
: rustc_span
::Span
,
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()
586 impl Visitor
<'tcx
> for DeadVisitor
<'tcx
> {
587 type Map
= Map
<'tcx
>;
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())
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
)
622 let participle
= match item
.kind
{
623 hir
::ItemKind
::Struct(..) => "constructed", // Issue #52325
626 self.warn_dead_code(item
.hir_id(), span
, item
.ident
.name
, participle
);
628 // Only continue if we didn't warn
629 intravisit
::walk_item(self, item
);
635 variant
: &'tcx hir
::Variant
<'tcx
>,
636 g
: &'tcx hir
::Generics
<'tcx
>,
639 if self.should_warn_about_variant(&variant
) {
640 self.warn_dead_code(variant
.id
, variant
.span
, variant
.ident
.name
, "constructed");
642 intravisit
::walk_variant(self, variant
, g
, id
);
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");
650 intravisit
::walk_foreign_item(self, fi
);
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");
657 intravisit
::walk_field_def(self, field
);
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()) {
667 impl_item
.ident
.name
,
671 self.visit_nested_body(body_id
)
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
)
686 self.warn_dead_code(impl_item
.hir_id(), span
, impl_item
.ident
.name
, "used");
688 self.visit_nested_body(body_id
)
690 hir
::ImplItemKind
::TyAlias(..) => {}
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
)
701 hir
::TraitItemKind
::Const(_
, None
)
702 | hir
::TraitItemKind
::Fn(_
, hir
::TraitFn
::Required(_
))
703 | hir
::TraitItemKind
::Type(..) => {}
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
);