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
;
19 use rustc_span
::symbol
::{sym, Symbol}
;
21 // Any local node that may call something in its body block should be
22 // explored. For example, if it's a live Node::Item that is a
23 // function, then we should explore its block to check for codes that
24 // may need to be marked as live.
25 fn should_explore(tcx
: TyCtxt
<'_
>, hir_id
: hir
::HirId
) -> bool
{
26 match tcx
.hir().find(hir_id
) {
30 | Node
::ForeignItem(..)
40 struct MarkSymbolVisitor
<'tcx
> {
41 worklist
: Vec
<hir
::HirId
>,
43 maybe_typeck_results
: Option
<&'tcx ty
::TypeckResults
<'tcx
>>,
44 live_symbols
: FxHashSet
<hir
::HirId
>,
45 repr_has_repr_c
: bool
,
47 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
);
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 fn handle_field_pattern_match(
141 pats
: &[hir
::FieldPat
<'_
>],
143 let variant
= match self.typeck_results().node_type(lhs
.hir_id
).kind
{
144 ty
::Adt(adt
, _
) => adt
.variant_of_res(res
),
145 _
=> span_bug
!(lhs
.span
, "non-ADT in struct pattern"),
148 if let PatKind
::Wild
= pat
.pat
.kind
{
151 let index
= self.tcx
.field_index(pat
.hir_id
, self.typeck_results());
152 self.insert_def_id(variant
.fields
[index
].did
);
156 fn mark_live_symbols(&mut self) {
157 let mut scanned
= FxHashSet
::default();
158 while let Some(id
) = self.worklist
.pop() {
159 if !scanned
.insert(id
) {
163 // in the case of tuple struct constructors we want to check the item, not the generated
164 // tuple struct constructor function
165 let id
= self.struct_constructors
.get(&id
).cloned().unwrap_or(id
);
167 if let Some(node
) = self.tcx
.hir().find(id
) {
168 self.live_symbols
.insert(id
);
169 self.visit_node(node
);
174 fn visit_node(&mut self, node
: Node
<'tcx
>) {
175 let had_repr_c
= self.repr_has_repr_c
;
176 self.repr_has_repr_c
= false;
177 let had_inherited_pub_visibility
= self.inherited_pub_visibility
;
178 self.inherited_pub_visibility
= false;
180 Node
::Item(item
) => match item
.kind
{
181 hir
::ItemKind
::Struct(..) | hir
::ItemKind
::Union(..) => {
182 let def_id
= self.tcx
.hir().local_def_id(item
.hir_id
);
183 let def
= self.tcx
.adt_def(def_id
);
184 self.repr_has_repr_c
= def
.repr
.c();
186 intravisit
::walk_item(self, &item
);
188 hir
::ItemKind
::Enum(..) => {
189 self.inherited_pub_visibility
= item
.vis
.node
.is_pub();
191 intravisit
::walk_item(self, &item
);
193 hir
::ItemKind
::ForeignMod(..) => {}
195 intravisit
::walk_item(self, &item
);
198 Node
::TraitItem(trait_item
) => {
199 intravisit
::walk_trait_item(self, trait_item
);
201 Node
::ImplItem(impl_item
) => {
202 intravisit
::walk_impl_item(self, impl_item
);
204 Node
::ForeignItem(foreign_item
) => {
205 intravisit
::walk_foreign_item(self, &foreign_item
);
209 self.repr_has_repr_c
= had_repr_c
;
210 self.inherited_pub_visibility
= had_inherited_pub_visibility
;
213 fn mark_as_used_if_union(&mut self, adt
: &ty
::AdtDef
, fields
: &[hir
::Field
<'_
>]) {
214 if adt
.is_union() && adt
.non_enum_variant().fields
.len() > 1 && adt
.did
.is_local() {
215 for field
in fields
{
216 let index
= self.tcx
.field_index(field
.hir_id
, self.typeck_results());
217 self.insert_def_id(adt
.non_enum_variant().fields
[index
].did
);
223 impl<'tcx
> Visitor
<'tcx
> for MarkSymbolVisitor
<'tcx
> {
224 type Map
= intravisit
::ErasedMap
<'tcx
>;
226 fn nested_visit_map(&mut self) -> intravisit
::NestedVisitorMap
<Self::Map
> {
227 NestedVisitorMap
::None
230 fn visit_nested_body(&mut self, body
: hir
::BodyId
) {
231 let old_maybe_typeck_results
=
232 self.maybe_typeck_results
.replace(self.tcx
.typeck_body(body
));
233 let body
= self.tcx
.hir().body(body
);
234 self.visit_body(body
);
235 self.maybe_typeck_results
= old_maybe_typeck_results
;
238 fn visit_variant_data(
240 def
: &'tcx hir
::VariantData
<'tcx
>,
242 _
: &hir
::Generics
<'_
>,
246 let has_repr_c
= self.repr_has_repr_c
;
247 let inherited_pub_visibility
= self.inherited_pub_visibility
;
248 let live_fields
= def
251 .filter(|f
| has_repr_c
|| inherited_pub_visibility
|| f
.vis
.node
.is_pub());
252 self.live_symbols
.extend(live_fields
.map(|f
| f
.hir_id
));
254 intravisit
::walk_struct_def(self, def
);
257 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
<'tcx
>) {
259 hir
::ExprKind
::Path(ref qpath @ hir
::QPath
::TypeRelative(..)) => {
260 let res
= self.typeck_results().qpath_res(qpath
, expr
.hir_id
);
261 self.handle_res(res
);
263 hir
::ExprKind
::MethodCall(..) => {
264 self.lookup_and_handle_method(expr
.hir_id
);
266 hir
::ExprKind
::Field(ref lhs
, ..) => {
267 self.handle_field_access(&lhs
, expr
.hir_id
);
269 hir
::ExprKind
::Struct(ref qpath
, ref fields
, _
) => {
270 let res
= self.typeck_results().qpath_res(qpath
, expr
.hir_id
);
271 self.handle_res(res
);
272 if let ty
::Adt(ref adt
, _
) = self.typeck_results().expr_ty(expr
).kind
{
273 self.mark_as_used_if_union(adt
, fields
);
279 intravisit
::walk_expr(self, expr
);
282 fn visit_arm(&mut self, arm
: &'tcx hir
::Arm
<'tcx
>) {
283 // Inside the body, ignore constructions of variants
284 // necessary for the pattern to match. Those construction sites
285 // can't be reached unless the variant is constructed elsewhere.
286 let len
= self.ignore_variant_stack
.len();
287 self.ignore_variant_stack
.extend(arm
.pat
.necessary_variants());
288 intravisit
::walk_arm(self, arm
);
289 self.ignore_variant_stack
.truncate(len
);
292 fn visit_pat(&mut self, pat
: &'tcx hir
::Pat
<'tcx
>) {
294 PatKind
::Struct(ref path
, ref fields
, _
) => {
295 let res
= self.typeck_results().qpath_res(path
, pat
.hir_id
);
296 self.handle_field_pattern_match(pat
, res
, fields
);
298 PatKind
::Path(ref qpath
) => {
299 let res
= self.typeck_results().qpath_res(qpath
, pat
.hir_id
);
300 self.handle_res(res
);
306 intravisit
::walk_pat(self, pat
);
310 fn visit_path(&mut self, path
: &'tcx hir
::Path
<'tcx
>, _
: hir
::HirId
) {
311 self.handle_res(path
.res
);
312 intravisit
::walk_path(self, path
);
315 fn visit_ty(&mut self, ty
: &'tcx hir
::Ty
<'tcx
>) {
316 if let TyKind
::OpaqueDef(item_id
, _
) = ty
.kind
{
317 let item
= self.tcx
.hir().expect_item(item_id
.id
);
318 intravisit
::walk_item(self, item
);
320 intravisit
::walk_ty(self, ty
);
323 fn visit_anon_const(&mut self, c
: &'tcx hir
::AnonConst
) {
324 self.live_symbols
.insert(c
.hir_id
);
325 intravisit
::walk_anon_const(self, c
);
329 fn has_allow_dead_code_or_lang_attr(
332 attrs
: &[ast
::Attribute
],
334 if tcx
.sess
.contains_name(attrs
, sym
::lang
) {
338 // Stable attribute for #[lang = "panic_impl"]
339 if tcx
.sess
.contains_name(attrs
, sym
::panic_handler
) {
343 // (To be) stable attribute for #[lang = "oom"]
344 if tcx
.sess
.contains_name(attrs
, sym
::alloc_error_handler
) {
348 let def_id
= tcx
.hir().local_def_id(id
);
349 let cg_attrs
= tcx
.codegen_fn_attrs(def_id
);
351 // #[used], #[no_mangle], #[export_name], etc also keeps the item alive
352 // forcefully, e.g., for placing it in a specific section.
353 if cg_attrs
.contains_extern_indicator() || cg_attrs
.flags
.contains(CodegenFnAttrFlags
::USED
) {
357 tcx
.lint_level_at_node(lint
::builtin
::DEAD_CODE
, id
).0 == lint
::Allow
360 // This visitor seeds items that
361 // 1) We want to explicitly consider as live:
362 // * Item annotated with #[allow(dead_code)]
363 // - This is done so that if we want to suppress warnings for a
364 // group of dead functions, we only have to annotate the "root".
365 // For example, if both `f` and `g` are dead and `f` calls `g`,
366 // then annotating `f` with `#[allow(dead_code)]` will suppress
367 // warning for both `f` and `g`.
368 // * Item annotated with #[lang=".."]
369 // - This is because lang items are always callable from elsewhere.
371 // 2) We are not sure to be live or not
372 // * Implementation of a trait method
373 struct LifeSeeder
<'k
, 'tcx
> {
374 worklist
: Vec
<hir
::HirId
>,
375 krate
: &'k hir
::Crate
<'k
>,
377 // see `MarkSymbolVisitor::struct_constructors`
378 struct_constructors
: FxHashMap
<hir
::HirId
, hir
::HirId
>,
381 impl<'v
, 'k
, 'tcx
> ItemLikeVisitor
<'v
> for LifeSeeder
<'k
, 'tcx
> {
382 fn visit_item(&mut self, item
: &hir
::Item
<'_
>) {
383 let allow_dead_code
= has_allow_dead_code_or_lang_attr(self.tcx
, item
.hir_id
, &item
.attrs
);
385 self.worklist
.push(item
.hir_id
);
388 hir
::ItemKind
::Enum(ref enum_def
, _
) => {
390 self.worklist
.extend(enum_def
.variants
.iter().map(|variant
| variant
.id
));
393 for variant
in enum_def
.variants
{
394 if let Some(ctor_hir_id
) = variant
.data
.ctor_hir_id() {
395 self.struct_constructors
.insert(ctor_hir_id
, variant
.id
);
399 hir
::ItemKind
::Trait(.., trait_item_refs
) => {
400 for trait_item_ref
in trait_item_refs
{
401 let trait_item
= self.krate
.trait_item(trait_item_ref
.id
);
402 match trait_item
.kind
{
403 hir
::TraitItemKind
::Const(_
, Some(_
))
404 | hir
::TraitItemKind
::Fn(_
, hir
::TraitFn
::Provided(_
)) => {
405 if has_allow_dead_code_or_lang_attr(
410 self.worklist
.push(trait_item
.hir_id
);
417 hir
::ItemKind
::Impl { ref of_trait, items, .. }
=> {
418 for impl_item_ref
in items
{
419 let impl_item
= self.krate
.impl_item(impl_item_ref
.id
);
420 if of_trait
.is_some()
421 || has_allow_dead_code_or_lang_attr(
427 self.worklist
.push(impl_item_ref
.id
.hir_id
);
431 hir
::ItemKind
::Struct(ref variant_data
, _
) => {
432 if let Some(ctor_hir_id
) = variant_data
.ctor_hir_id() {
433 self.struct_constructors
.insert(ctor_hir_id
, item
.hir_id
);
440 fn visit_trait_item(&mut self, _item
: &hir
::TraitItem
<'_
>) {
441 // ignore: we are handling this in `visit_item` above
444 fn visit_impl_item(&mut self, _item
: &hir
::ImplItem
<'_
>) {
445 // ignore: we are handling this in `visit_item` above
449 fn create_and_seed_worklist
<'tcx
>(
451 access_levels
: &privacy
::AccessLevels
,
452 krate
: &hir
::Crate
<'_
>,
453 ) -> (Vec
<hir
::HirId
>, FxHashMap
<hir
::HirId
, hir
::HirId
>) {
454 let worklist
= access_levels
459 if level
>= &privacy
::AccessLevel
::Reachable { Some(id) }
else { None }
464 tcx
.entry_fn(LOCAL_CRATE
).map(|(def_id
, _
)| tcx
.hir().local_def_id_to_hir_id(def_id
)),
466 .collect
::<Vec
<_
>>();
468 // Seed implemented trait items
469 let mut life_seeder
=
470 LifeSeeder { worklist, krate, tcx, struct_constructors: Default::default() }
;
471 krate
.visit_all_item_likes(&mut life_seeder
);
473 (life_seeder
.worklist
, life_seeder
.struct_constructors
)
478 access_levels
: &privacy
::AccessLevels
,
479 krate
: &hir
::Crate
<'_
>,
480 ) -> FxHashSet
<hir
::HirId
> {
481 let (worklist
, struct_constructors
) = create_and_seed_worklist(tcx
, access_levels
, krate
);
482 let mut symbol_visitor
= MarkSymbolVisitor
{
485 maybe_typeck_results
: None
,
486 live_symbols
: Default
::default(),
487 repr_has_repr_c
: false,
489 inherited_pub_visibility
: false,
490 ignore_variant_stack
: vec
![],
493 symbol_visitor
.mark_live_symbols();
494 symbol_visitor
.live_symbols
497 struct DeadVisitor
<'tcx
> {
499 live_symbols
: FxHashSet
<hir
::HirId
>,
502 impl DeadVisitor
<'tcx
> {
503 fn should_warn_about_item(&mut self, item
: &hir
::Item
<'_
>) -> bool
{
504 let should_warn
= match item
.kind
{
505 hir
::ItemKind
::Static(..)
506 | hir
::ItemKind
::Const(..)
507 | hir
::ItemKind
::Fn(..)
508 | hir
::ItemKind
::TyAlias(..)
509 | hir
::ItemKind
::Enum(..)
510 | hir
::ItemKind
::Struct(..)
511 | hir
::ItemKind
::Union(..) => true,
514 should_warn
&& !self.symbol_is_live(item
.hir_id
)
517 fn should_warn_about_field(&mut self, field
: &hir
::StructField
<'_
>) -> bool
{
518 let field_type
= self.tcx
.type_of(self.tcx
.hir().local_def_id(field
.hir_id
));
519 !field
.is_positional()
520 && !self.symbol_is_live(field
.hir_id
)
521 && !field_type
.is_phantom_data()
522 && !has_allow_dead_code_or_lang_attr(self.tcx
, field
.hir_id
, &field
.attrs
)
525 fn should_warn_about_variant(&mut self, variant
: &hir
::Variant
<'_
>) -> bool
{
526 !self.symbol_is_live(variant
.id
)
527 && !has_allow_dead_code_or_lang_attr(self.tcx
, variant
.id
, &variant
.attrs
)
530 fn should_warn_about_foreign_item(&mut self, fi
: &hir
::ForeignItem
<'_
>) -> bool
{
531 !self.symbol_is_live(fi
.hir_id
)
532 && !has_allow_dead_code_or_lang_attr(self.tcx
, fi
.hir_id
, &fi
.attrs
)
535 // id := HIR id of an item's definition.
536 fn symbol_is_live(&mut self, id
: hir
::HirId
) -> bool
{
537 if self.live_symbols
.contains(&id
) {
540 // If it's a type whose items are live, then it's live, too.
541 // This is done to handle the case where, for example, the static
542 // method of a private type is used, but the type itself is never
544 let def_id
= self.tcx
.hir().local_def_id(id
);
545 let inherent_impls
= self.tcx
.inherent_impls(def_id
);
546 for &impl_did
in inherent_impls
.iter() {
547 for &item_did
in &self.tcx
.associated_item_def_ids(impl_did
)[..] {
548 if let Some(did
) = item_did
.as_local() {
549 let item_hir_id
= self.tcx
.hir().local_def_id_to_hir_id(did
);
550 if self.live_symbols
.contains(&item_hir_id
) {
562 span
: rustc_span
::Span
,
566 if !name
.as_str().starts_with('_'
) {
567 self.tcx
.struct_span_lint_hir(lint
::builtin
::DEAD_CODE
, id
, span
, |lint
| {
568 let def_id
= self.tcx
.hir().local_def_id(id
);
569 let descr
= self.tcx
.def_kind(def_id
).descr(def_id
.to_def_id());
570 lint
.build(&format
!("{} is never {}: `{}`", descr
, participle
, name
)).emit()
576 impl Visitor
<'tcx
> for DeadVisitor
<'tcx
> {
577 type Map
= Map
<'tcx
>;
579 /// Walk nested items in place so that we don't report dead-code
580 /// on inner functions when the outer function is already getting
581 /// an error. We could do this also by checking the parents, but
582 /// this is how the code is setup and it seems harmless enough.
583 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
584 NestedVisitorMap
::All(self.tcx
.hir())
587 fn visit_item(&mut self, item
: &'tcx hir
::Item
<'tcx
>) {
588 if self.should_warn_about_item(item
) {
589 // For most items, we want to highlight its identifier
590 let span
= match item
.kind
{
591 hir
::ItemKind
::Fn(..)
592 | hir
::ItemKind
::Mod(..)
593 | hir
::ItemKind
::Enum(..)
594 | hir
::ItemKind
::Struct(..)
595 | hir
::ItemKind
::Union(..)
596 | hir
::ItemKind
::Trait(..)
597 | hir
::ItemKind
::Impl { .. }
=> {
598 // FIXME(66095): Because item.span is annotated with things
599 // like expansion data, and ident.span isn't, we use the
600 // def_span method if it's part of a macro invocation
601 // (and thus has a source_callee set).
602 // We should probably annotate ident.span with the macro
603 // context, but that's a larger change.
604 if item
.span
.source_callee().is_some() {
605 self.tcx
.sess
.source_map().guess_head_span(item
.span
)
612 let participle
= match item
.kind
{
613 hir
::ItemKind
::Struct(..) => "constructed", // Issue #52325
616 self.warn_dead_code(item
.hir_id
, span
, item
.ident
.name
, participle
);
618 // Only continue if we didn't warn
619 intravisit
::walk_item(self, item
);
625 variant
: &'tcx hir
::Variant
<'tcx
>,
626 g
: &'tcx hir
::Generics
<'tcx
>,
629 if self.should_warn_about_variant(&variant
) {
630 self.warn_dead_code(variant
.id
, variant
.span
, variant
.ident
.name
, "constructed");
632 intravisit
::walk_variant(self, variant
, g
, id
);
636 fn visit_foreign_item(&mut self, fi
: &'tcx hir
::ForeignItem
<'tcx
>) {
637 if self.should_warn_about_foreign_item(fi
) {
638 self.warn_dead_code(fi
.hir_id
, fi
.span
, fi
.ident
.name
, "used");
640 intravisit
::walk_foreign_item(self, fi
);
643 fn visit_struct_field(&mut self, field
: &'tcx hir
::StructField
<'tcx
>) {
644 if self.should_warn_about_field(&field
) {
645 self.warn_dead_code(field
.hir_id
, field
.span
, field
.ident
.name
, "read");
647 intravisit
::walk_struct_field(self, field
);
650 fn visit_impl_item(&mut self, impl_item
: &'tcx hir
::ImplItem
<'tcx
>) {
651 match impl_item
.kind
{
652 hir
::ImplItemKind
::Const(_
, body_id
) => {
653 if !self.symbol_is_live(impl_item
.hir_id
) {
657 impl_item
.ident
.name
,
661 self.visit_nested_body(body_id
)
663 hir
::ImplItemKind
::Fn(_
, body_id
) => {
664 if !self.symbol_is_live(impl_item
.hir_id
) {
665 // FIXME(66095): Because impl_item.span is annotated with things
666 // like expansion data, and ident.span isn't, we use the
667 // def_span method if it's part of a macro invocation
668 // (and thus has a source_callee set).
669 // We should probably annotate ident.span with the macro
670 // context, but that's a larger change.
671 let span
= if impl_item
.span
.source_callee().is_some() {
672 self.tcx
.sess
.source_map().guess_head_span(impl_item
.span
)
676 self.warn_dead_code(impl_item
.hir_id
, span
, impl_item
.ident
.name
, "used");
678 self.visit_nested_body(body_id
)
680 hir
::ImplItemKind
::TyAlias(..) => {}
684 // Overwrite so that we don't warn the trait item itself.
685 fn visit_trait_item(&mut self, trait_item
: &'tcx hir
::TraitItem
<'tcx
>) {
686 match trait_item
.kind
{
687 hir
::TraitItemKind
::Const(_
, Some(body_id
))
688 | hir
::TraitItemKind
::Fn(_
, hir
::TraitFn
::Provided(body_id
)) => {
689 self.visit_nested_body(body_id
)
691 hir
::TraitItemKind
::Const(_
, None
)
692 | hir
::TraitItemKind
::Fn(_
, hir
::TraitFn
::Required(_
))
693 | hir
::TraitItemKind
::Type(..) => {}
698 pub fn check_crate(tcx
: TyCtxt
<'_
>) {
699 let access_levels
= &tcx
.privacy_access_levels(LOCAL_CRATE
);
700 let krate
= tcx
.hir().krate();
701 let live_symbols
= find_live(tcx
, access_levels
, krate
);
702 let mut visitor
= DeadVisitor { tcx, live_symbols }
;
703 intravisit
::walk_crate(&mut visitor
, krate
);