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.
6 use hir
::{self, PatKind}
;
7 use hir
::intravisit
::{self, Visitor, NestedVisitorMap}
;
8 use hir
::itemlikevisit
::ItemLikeVisitor
;
11 use hir
::CodegenFnAttrFlags
;
12 use hir
::def_id
::{DefId, LOCAL_CRATE}
;
15 use ty
::{self, TyCtxt}
;
16 use util
::nodemap
::FxHashSet
;
18 use rustc_data_structures
::fx
::FxHashMap
;
20 use syntax
::{ast, source_map}
;
24 // Any local node that may call something in its body block should be
25 // explored. For example, if it's a live Node::Item that is a
26 // function, then we should explore its block to check for codes that
27 // may need to be marked as live.
28 fn should_explore
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
29 node_id
: ast
::NodeId
) -> bool
{
30 match tcx
.hir().find(node_id
) {
31 Some(Node
::Item(..)) |
32 Some(Node
::ImplItem(..)) |
33 Some(Node
::ForeignItem(..)) |
34 Some(Node
::TraitItem(..)) =>
41 struct MarkSymbolVisitor
<'a
, 'tcx
: 'a
> {
42 worklist
: Vec
<ast
::NodeId
>,
43 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
44 tables
: &'a ty
::TypeckTables
<'tcx
>,
45 live_symbols
: FxHashSet
<ast
::NodeId
>,
46 repr_has_repr_c
: bool
,
48 inherited_pub_visibility
: bool
,
49 ignore_variant_stack
: Vec
<DefId
>,
50 // maps from tuple struct constructors to tuple struct items
51 struct_constructors
: FxHashMap
<ast
::NodeId
, ast
::NodeId
>,
54 impl<'a
, 'tcx
> MarkSymbolVisitor
<'a
, 'tcx
> {
55 fn check_def_id(&mut self, def_id
: DefId
) {
56 if let Some(node_id
) = self.tcx
.hir().as_local_node_id(def_id
) {
57 if should_explore(self.tcx
, node_id
) ||
58 self.struct_constructors
.contains_key(&node_id
) {
59 self.worklist
.push(node_id
);
61 self.live_symbols
.insert(node_id
);
65 fn insert_def_id(&mut self, def_id
: DefId
) {
66 if let Some(node_id
) = self.tcx
.hir().as_local_node_id(def_id
) {
67 debug_assert
!(!should_explore(self.tcx
, node_id
));
68 self.live_symbols
.insert(node_id
);
72 fn handle_definition(&mut self, def
: Def
) {
74 Def
::Const(_
) | Def
::AssociatedConst(..) | Def
::TyAlias(_
) => {
75 self.check_def_id(def
.def_id());
77 _
if self.in_pat
=> (),
78 Def
::PrimTy(..) | Def
::SelfTy(..) | Def
::SelfCtor(..) |
79 Def
::Local(..) | Def
::Upvar(..) => {}
80 Def
::Variant(variant_id
) | Def
::VariantCtor(variant_id
, ..) => {
81 if let Some(enum_id
) = self.tcx
.parent_def_id(variant_id
) {
82 self.check_def_id(enum_id
);
84 if !self.ignore_variant_stack
.contains(&variant_id
) {
85 self.check_def_id(variant_id
);
89 self.check_def_id(def
.def_id());
94 fn lookup_and_handle_method(&mut self, id
: hir
::HirId
) {
95 if let Some(def
) = self.tables
.type_dependent_defs().get(id
) {
96 self.check_def_id(def
.def_id());
98 bug
!("no type-dependent def for method");
102 fn handle_field_access(&mut self, lhs
: &hir
::Expr
, node_id
: ast
::NodeId
) {
103 match self.tables
.expr_ty_adjusted(lhs
).sty
{
105 let index
= self.tcx
.field_index(node_id
, self.tables
);
106 self.insert_def_id(def
.non_enum_variant().fields
[index
].did
);
109 _
=> span_bug
!(lhs
.span
, "named field access on non-ADT"),
113 fn handle_field_pattern_match(&mut self, lhs
: &hir
::Pat
, def
: Def
,
114 pats
: &[source_map
::Spanned
<hir
::FieldPat
>]) {
115 let variant
= match self.tables
.node_id_to_type(lhs
.hir_id
).sty
{
116 ty
::Adt(adt
, _
) => adt
.variant_of_def(def
),
117 _
=> span_bug
!(lhs
.span
, "non-ADT in struct pattern")
120 if let PatKind
::Wild
= pat
.node
.pat
.node
{
123 let index
= self.tcx
.field_index(pat
.node
.id
, self.tables
);
124 self.insert_def_id(variant
.fields
[index
].did
);
128 fn mark_live_symbols(&mut self) {
129 let mut scanned
= FxHashSet
::default();
130 while let Some(id
) = self.worklist
.pop() {
131 if !scanned
.insert(id
) {
135 // in the case of tuple struct constructors we want to check the item, not the generated
136 // tuple struct constructor function
137 let id
= self.struct_constructors
.get(&id
).cloned().unwrap_or(id
);
139 if let Some(node
) = self.tcx
.hir().find(id
) {
140 self.live_symbols
.insert(id
);
141 self.visit_node(node
);
146 fn visit_node(&mut self, node
: Node
<'tcx
>) {
147 let had_repr_c
= self.repr_has_repr_c
;
148 self.repr_has_repr_c
= false;
149 let had_inherited_pub_visibility
= self.inherited_pub_visibility
;
150 self.inherited_pub_visibility
= false;
152 Node
::Item(item
) => {
154 hir
::ItemKind
::Struct(..) | hir
::ItemKind
::Union(..) => {
155 let def_id
= self.tcx
.hir().local_def_id(item
.id
);
156 let def
= self.tcx
.adt_def(def_id
);
157 self.repr_has_repr_c
= def
.repr
.c();
159 intravisit
::walk_item(self, &item
);
161 hir
::ItemKind
::Enum(..) => {
162 self.inherited_pub_visibility
= item
.vis
.node
.is_pub();
163 intravisit
::walk_item(self, &item
);
165 hir
::ItemKind
::Fn(..)
166 | hir
::ItemKind
::Ty(..)
167 | hir
::ItemKind
::Static(..)
168 | hir
::ItemKind
::Existential(..)
169 | hir
::ItemKind
::Const(..) => {
170 intravisit
::walk_item(self, &item
);
175 Node
::TraitItem(trait_item
) => {
176 intravisit
::walk_trait_item(self, trait_item
);
178 Node
::ImplItem(impl_item
) => {
179 intravisit
::walk_impl_item(self, impl_item
);
181 Node
::ForeignItem(foreign_item
) => {
182 intravisit
::walk_foreign_item(self, &foreign_item
);
186 self.repr_has_repr_c
= had_repr_c
;
187 self.inherited_pub_visibility
= had_inherited_pub_visibility
;
190 fn mark_as_used_if_union(&mut self, adt
: &ty
::AdtDef
, fields
: &hir
::HirVec
<hir
::Field
>) {
191 if adt
.is_union() && adt
.non_enum_variant().fields
.len() > 1 && adt
.did
.is_local() {
192 for field
in fields
{
193 let index
= self.tcx
.field_index(field
.id
, self.tables
);
194 self.insert_def_id(adt
.non_enum_variant().fields
[index
].did
);
200 impl<'a
, 'tcx
> Visitor
<'tcx
> for MarkSymbolVisitor
<'a
, 'tcx
> {
201 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
202 NestedVisitorMap
::None
205 fn visit_nested_body(&mut self, body
: hir
::BodyId
) {
206 let old_tables
= self.tables
;
207 self.tables
= self.tcx
.body_tables(body
);
208 let body
= self.tcx
.hir().body(body
);
209 self.visit_body(body
);
210 self.tables
= old_tables
;
213 fn visit_variant_data(&mut self, def
: &'tcx hir
::VariantData
, _
: ast
::Name
,
214 _
: &hir
::Generics
, _
: ast
::NodeId
, _
: syntax_pos
::Span
) {
215 let has_repr_c
= self.repr_has_repr_c
;
216 let inherited_pub_visibility
= self.inherited_pub_visibility
;
217 let live_fields
= def
.fields().iter().filter(|f
| {
218 has_repr_c
|| inherited_pub_visibility
|| f
.vis
.node
.is_pub()
220 self.live_symbols
.extend(live_fields
.map(|f
| f
.id
));
222 intravisit
::walk_struct_def(self, def
);
225 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
) {
227 hir
::ExprKind
::Path(ref qpath @ hir
::QPath
::TypeRelative(..)) => {
228 let def
= self.tables
.qpath_def(qpath
, expr
.hir_id
);
229 self.handle_definition(def
);
231 hir
::ExprKind
::MethodCall(..) => {
232 self.lookup_and_handle_method(expr
.hir_id
);
234 hir
::ExprKind
::Field(ref lhs
, ..) => {
235 self.handle_field_access(&lhs
, expr
.id
);
237 hir
::ExprKind
::Struct(_
, ref fields
, _
) => {
238 if let ty
::Adt(ref adt
, _
) = self.tables
.expr_ty(expr
).sty
{
239 self.mark_as_used_if_union(adt
, fields
);
245 intravisit
::walk_expr(self, expr
);
248 fn visit_arm(&mut self, arm
: &'tcx hir
::Arm
) {
249 if arm
.pats
.len() == 1 {
250 let variants
= arm
.pats
[0].necessary_variants();
252 // Inside the body, ignore constructions of variants
253 // necessary for the pattern to match. Those construction sites
254 // can't be reached unless the variant is constructed elsewhere.
255 let len
= self.ignore_variant_stack
.len();
256 self.ignore_variant_stack
.extend_from_slice(&variants
);
257 intravisit
::walk_arm(self, arm
);
258 self.ignore_variant_stack
.truncate(len
);
260 intravisit
::walk_arm(self, arm
);
264 fn visit_pat(&mut self, pat
: &'tcx hir
::Pat
) {
266 PatKind
::Struct(hir
::QPath
::Resolved(_
, ref path
), ref fields
, _
) => {
267 self.handle_field_pattern_match(pat
, path
.def
, fields
);
269 PatKind
::Path(ref qpath @ hir
::QPath
::TypeRelative(..)) => {
270 let def
= self.tables
.qpath_def(qpath
, pat
.hir_id
);
271 self.handle_definition(def
);
277 intravisit
::walk_pat(self, pat
);
281 fn visit_path(&mut self, path
: &'tcx hir
::Path
, _
: hir
::HirId
) {
282 self.handle_definition(path
.def
);
283 intravisit
::walk_path(self, path
);
287 fn has_allow_dead_code_or_lang_attr(tcx
: TyCtxt
<'_
, '_
, '_
>,
289 attrs
: &[ast
::Attribute
]) -> bool
{
290 if attr
::contains_name(attrs
, "lang") {
294 // Stable attribute for #[lang = "panic_impl"]
295 if attr
::contains_name(attrs
, "panic_handler") {
299 // (To be) stable attribute for #[lang = "oom"]
300 if attr
::contains_name(attrs
, "alloc_error_handler") {
304 // Don't lint about global allocators
305 if attr
::contains_name(attrs
, "global_allocator") {
309 let def_id
= tcx
.hir().local_def_id(id
);
310 let cg_attrs
= tcx
.codegen_fn_attrs(def_id
);
312 // #[used], #[no_mangle], #[export_name], etc also keeps the item alive
313 // forcefully, e.g., for placing it in a specific section.
314 if cg_attrs
.contains_extern_indicator() ||
315 cg_attrs
.flags
.contains(CodegenFnAttrFlags
::USED
) {
319 tcx
.lint_level_at_node(lint
::builtin
::DEAD_CODE
, id
).0 == lint
::Allow
322 // This visitor seeds items that
323 // 1) We want to explicitly consider as live:
324 // * Item annotated with #[allow(dead_code)]
325 // - This is done so that if we want to suppress warnings for a
326 // group of dead functions, we only have to annotate the "root".
327 // For example, if both `f` and `g` are dead and `f` calls `g`,
328 // then annotating `f` with `#[allow(dead_code)]` will suppress
329 // warning for both `f` and `g`.
330 // * Item annotated with #[lang=".."]
331 // - This is because lang items are always callable from elsewhere.
333 // 2) We are not sure to be live or not
334 // * Implementation of a trait method
335 struct LifeSeeder
<'k
, 'tcx
: 'k
> {
336 worklist
: Vec
<ast
::NodeId
>,
337 krate
: &'k hir
::Crate
,
338 tcx
: TyCtxt
<'k
, 'tcx
, 'tcx
>,
339 // see `MarkSymbolVisitor::struct_constructors`
340 struct_constructors
: FxHashMap
<ast
::NodeId
, ast
::NodeId
>,
343 impl<'v
, 'k
, 'tcx
> ItemLikeVisitor
<'v
> for LifeSeeder
<'k
, 'tcx
> {
344 fn visit_item(&mut self, item
: &hir
::Item
) {
345 let allow_dead_code
= has_allow_dead_code_or_lang_attr(self.tcx
,
349 self.worklist
.push(item
.id
);
352 hir
::ItemKind
::Enum(ref enum_def
, _
) if allow_dead_code
=> {
353 self.worklist
.extend(enum_def
.variants
.iter()
354 .map(|variant
| variant
.node
.data
.id()));
356 hir
::ItemKind
::Trait(.., ref trait_item_refs
) => {
357 for trait_item_ref
in trait_item_refs
{
358 let trait_item
= self.krate
.trait_item(trait_item_ref
.id
);
359 match trait_item
.node
{
360 hir
::TraitItemKind
::Const(_
, Some(_
)) |
361 hir
::TraitItemKind
::Method(_
, hir
::TraitMethod
::Provided(_
)) => {
362 if has_allow_dead_code_or_lang_attr(self.tcx
,
365 self.worklist
.push(trait_item
.id
);
372 hir
::ItemKind
::Impl(.., ref opt_trait
, _
, ref impl_item_refs
) => {
373 for impl_item_ref
in impl_item_refs
{
374 let impl_item
= self.krate
.impl_item(impl_item_ref
.id
);
375 if opt_trait
.is_some() ||
376 has_allow_dead_code_or_lang_attr(self.tcx
,
379 self.worklist
.push(impl_item_ref
.id
.node_id
);
383 hir
::ItemKind
::Struct(ref variant_data
, _
) => {
384 self.struct_constructors
.insert(variant_data
.id(), item
.id
);
390 fn visit_trait_item(&mut self, _item
: &hir
::TraitItem
) {
391 // ignore: we are handling this in `visit_item` above
394 fn visit_impl_item(&mut self, _item
: &hir
::ImplItem
) {
395 // ignore: we are handling this in `visit_item` above
399 fn create_and_seed_worklist
<'a
, 'tcx
>(
400 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
401 access_levels
: &privacy
::AccessLevels
,
403 ) -> (Vec
<ast
::NodeId
>, FxHashMap
<ast
::NodeId
, ast
::NodeId
>) {
404 let worklist
= access_levels
.map
.iter().filter_map(|(&id
, level
)| {
405 if level
>= &privacy
::AccessLevel
::Reachable
{
412 tcx
.sess
.entry_fn
.borrow().map(|(id
, _
, _
)| id
)
413 ).collect
::<Vec
<_
>>();
415 // Seed implemented trait items
416 let mut life_seeder
= LifeSeeder
{
420 struct_constructors
: Default
::default(),
422 krate
.visit_all_item_likes(&mut life_seeder
);
424 (life_seeder
.worklist
, life_seeder
.struct_constructors
)
427 fn find_live
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
428 access_levels
: &privacy
::AccessLevels
,
430 -> FxHashSet
<ast
::NodeId
> {
431 let (worklist
, struct_constructors
) = create_and_seed_worklist(tcx
, access_levels
, krate
);
432 let mut symbol_visitor
= MarkSymbolVisitor
{
435 tables
: &ty
::TypeckTables
::empty(None
),
436 live_symbols
: Default
::default(),
437 repr_has_repr_c
: false,
439 inherited_pub_visibility
: false,
440 ignore_variant_stack
: vec
![],
443 symbol_visitor
.mark_live_symbols();
444 symbol_visitor
.live_symbols
447 struct DeadVisitor
<'a
, 'tcx
: 'a
> {
448 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
449 live_symbols
: FxHashSet
<ast
::NodeId
>,
452 impl<'a
, 'tcx
> DeadVisitor
<'a
, 'tcx
> {
453 fn should_warn_about_item(&mut self, item
: &hir
::Item
) -> bool
{
454 let should_warn
= match item
.node
{
455 hir
::ItemKind
::Static(..)
456 | hir
::ItemKind
::Const(..)
457 | hir
::ItemKind
::Fn(..)
458 | hir
::ItemKind
::Ty(..)
459 | hir
::ItemKind
::Enum(..)
460 | hir
::ItemKind
::Struct(..)
461 | hir
::ItemKind
::Union(..) => true,
464 should_warn
&& !self.symbol_is_live(item
.id
)
467 fn should_warn_about_field(&mut self, field
: &hir
::StructField
) -> bool
{
468 let field_type
= self.tcx
.type_of(self.tcx
.hir().local_def_id(field
.id
));
469 !field
.is_positional()
470 && !self.symbol_is_live(field
.id
)
471 && !field_type
.is_phantom_data()
472 && !has_allow_dead_code_or_lang_attr(self.tcx
, field
.id
, &field
.attrs
)
475 fn should_warn_about_variant(&mut self, variant
: &hir
::VariantKind
) -> bool
{
476 !self.symbol_is_live(variant
.data
.id())
477 && !has_allow_dead_code_or_lang_attr(self.tcx
,
482 fn should_warn_about_foreign_item(&mut self, fi
: &hir
::ForeignItem
) -> bool
{
483 !self.symbol_is_live(fi
.id
)
484 && !has_allow_dead_code_or_lang_attr(self.tcx
, fi
.id
, &fi
.attrs
)
487 // id := node id of an item's definition.
492 if self.live_symbols
.contains(&id
) {
495 // If it's a type whose items are live, then it's live, too.
496 // This is done to handle the case where, for example, the static
497 // method of a private type is used, but the type itself is never
499 let def_id
= self.tcx
.hir().local_def_id(id
);
500 let inherent_impls
= self.tcx
.inherent_impls(def_id
);
501 for &impl_did
in inherent_impls
.iter() {
502 for &item_did
in &self.tcx
.associated_item_def_ids(impl_did
)[..] {
503 if let Some(item_node_id
) = self.tcx
.hir().as_local_node_id(item_did
) {
504 if self.live_symbols
.contains(&item_node_id
) {
513 fn warn_dead_code(&mut self,
515 span
: syntax_pos
::Span
,
519 if !name
.as_str().starts_with("_") {
521 .lint_node(lint
::builtin
::DEAD_CODE
,
524 &format
!("{} is never {}: `{}`",
525 node_type
, participle
, name
));
530 impl<'a
, 'tcx
> Visitor
<'tcx
> for DeadVisitor
<'a
, 'tcx
> {
531 /// Walk nested items in place so that we don't report dead-code
532 /// on inner functions when the outer function is already getting
533 /// an error. We could do this also by checking the parents, but
534 /// this is how the code is setup and it seems harmless enough.
535 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
536 NestedVisitorMap
::All(&self.tcx
.hir())
539 fn visit_item(&mut self, item
: &'tcx hir
::Item
) {
540 if self.should_warn_about_item(item
) {
541 // For items that have a definition with a signature followed by a
542 // block, point only at the signature.
543 let span
= match item
.node
{
544 hir
::ItemKind
::Fn(..) |
545 hir
::ItemKind
::Mod(..) |
546 hir
::ItemKind
::Enum(..) |
547 hir
::ItemKind
::Struct(..) |
548 hir
::ItemKind
::Union(..) |
549 hir
::ItemKind
::Trait(..) |
550 hir
::ItemKind
::Impl(..) => self.tcx
.sess
.source_map().def_span(item
.span
),
553 let participle
= match item
.node
{
554 hir
::ItemKind
::Struct(..) => "constructed", // Issue #52325
561 item
.node
.descriptive_variant(),
565 // Only continue if we didn't warn
566 intravisit
::walk_item(self, item
);
570 fn visit_variant(&mut self,
571 variant
: &'tcx hir
::Variant
,
572 g
: &'tcx hir
::Generics
,
574 if self.should_warn_about_variant(&variant
.node
) {
575 self.warn_dead_code(variant
.node
.data
.id(), variant
.span
, variant
.node
.ident
.name
,
576 "variant", "constructed");
578 intravisit
::walk_variant(self, variant
, g
, id
);
582 fn visit_foreign_item(&mut self, fi
: &'tcx hir
::ForeignItem
) {
583 if self.should_warn_about_foreign_item(fi
) {
584 self.warn_dead_code(fi
.id
, fi
.span
, fi
.ident
.name
,
585 fi
.node
.descriptive_variant(), "used");
587 intravisit
::walk_foreign_item(self, fi
);
590 fn visit_struct_field(&mut self, field
: &'tcx hir
::StructField
) {
591 if self.should_warn_about_field(&field
) {
592 self.warn_dead_code(field
.id
, field
.span
, field
.ident
.name
, "field", "used");
594 intravisit
::walk_struct_field(self, field
);
597 fn visit_impl_item(&mut self, impl_item
: &'tcx hir
::ImplItem
) {
598 match impl_item
.node
{
599 hir
::ImplItemKind
::Const(_
, body_id
) => {
600 if !self.symbol_is_live(impl_item
.id
) {
601 self.warn_dead_code(impl_item
.id
,
603 impl_item
.ident
.name
,
607 self.visit_nested_body(body_id
)
609 hir
::ImplItemKind
::Method(_
, body_id
) => {
610 if !self.symbol_is_live(impl_item
.id
) {
611 let span
= self.tcx
.sess
.source_map().def_span(impl_item
.span
);
612 self.warn_dead_code(impl_item
.id
, span
, impl_item
.ident
.name
, "method", "used");
614 self.visit_nested_body(body_id
)
616 hir
::ImplItemKind
::Existential(..) |
617 hir
::ImplItemKind
::Type(..) => {}
621 // Overwrite so that we don't warn the trait item itself.
622 fn visit_trait_item(&mut self, trait_item
: &'tcx hir
::TraitItem
) {
623 match trait_item
.node
{
624 hir
::TraitItemKind
::Const(_
, Some(body_id
)) |
625 hir
::TraitItemKind
::Method(_
, hir
::TraitMethod
::Provided(body_id
)) => {
626 self.visit_nested_body(body_id
)
628 hir
::TraitItemKind
::Const(_
, None
) |
629 hir
::TraitItemKind
::Method(_
, hir
::TraitMethod
::Required(_
)) |
630 hir
::TraitItemKind
::Type(..) => {}
635 pub fn check_crate
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>) {
636 let access_levels
= &tcx
.privacy_access_levels(LOCAL_CRATE
);
637 let krate
= tcx
.hir().krate();
638 let live_symbols
= find_live(tcx
, access_levels
, krate
);
639 let mut visitor
= DeadVisitor
{
643 intravisit
::walk_crate(&mut visitor
, krate
);