Skip to content

Commit 02a42ff

Browse files
committed
Rewrite dead-code pass to avoid fetching HIR.
1 parent a319f3c commit 02a42ff

File tree

5 files changed

+142
-198
lines changed

5 files changed

+142
-198
lines changed

compiler/rustc_passes/src/dead.rs

+89-168
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@ use rustc_hir::def::{CtorOf, DefKind, Res};
1010
use rustc_hir::def_id::{DefId, LocalDefId};
1111
use rustc_hir::intravisit::{self, Visitor};
1212
use rustc_hir::{Node, PatKind, TyKind};
13-
use rustc_middle::hir::nested_filter;
1413
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1514
use rustc_middle::middle::privacy;
1615
use rustc_middle::ty::query::Providers;
1716
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
1817
use rustc_session::lint;
1918
use rustc_span::symbol::{sym, Symbol};
20-
use rustc_span::Span;
2119
use std::mem;
2220

2321
// Any local node that may call something in its body block should be
@@ -647,41 +645,16 @@ struct DeadVisitor<'tcx> {
647645
}
648646

649647
impl<'tcx> DeadVisitor<'tcx> {
650-
fn should_warn_about_item(&mut self, item: &hir::Item<'_>) -> bool {
651-
let should_warn = matches!(
652-
item.kind,
653-
hir::ItemKind::Static(..)
654-
| hir::ItemKind::Const(..)
655-
| hir::ItemKind::Fn(..)
656-
| hir::ItemKind::TyAlias(..)
657-
| hir::ItemKind::Enum(..)
658-
| hir::ItemKind::Struct(..)
659-
| hir::ItemKind::Union(..)
660-
);
661-
should_warn && !self.symbol_is_live(item.def_id)
662-
}
663-
664-
fn should_warn_about_field(&mut self, field: &hir::FieldDef<'_>) -> bool {
665-
let def_id = self.tcx.hir().local_def_id(field.hir_id);
666-
let field_type = self.tcx.type_of(def_id);
667-
!field.is_positional()
668-
&& !self.symbol_is_live(def_id)
669-
&& !field_type.is_phantom_data()
670-
&& !has_allow_dead_code_or_lang_attr(self.tcx, field.hir_id)
671-
}
672-
673-
fn should_warn_about_variant(&mut self, variant: &hir::Variant<'_>) -> bool {
674-
let def_id = self.tcx.hir().local_def_id(variant.id);
675-
!self.symbol_is_live(def_id) && !has_allow_dead_code_or_lang_attr(self.tcx, variant.id)
676-
}
677-
678-
fn should_warn_about_foreign_item(&mut self, fi: &hir::ForeignItem<'_>) -> bool {
679-
!self.symbol_is_live(fi.def_id) && !has_allow_dead_code_or_lang_attr(self.tcx, fi.hir_id())
680-
}
681-
682-
// id := HIR id of an item's definition.
683-
fn symbol_is_live(&mut self, def_id: LocalDefId) -> bool {
684-
self.live_symbols.contains(&def_id)
648+
fn should_warn_about_field(&mut self, field: &ty::FieldDef) -> bool {
649+
if self.live_symbols.contains(&field.did.expect_local()) {
650+
return false;
651+
}
652+
let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit());
653+
if is_positional {
654+
return false;
655+
}
656+
let field_type = self.tcx.type_of(field.did);
657+
!field_type.is_phantom_data()
685658
}
686659

687660
fn warn_multiple_dead_codes(
@@ -790,154 +763,102 @@ impl<'tcx> DeadVisitor<'tcx> {
790763
}
791764

792765
fn warn_dead_code(&mut self, id: LocalDefId, participle: &str) {
793-
if self.tcx.item_name(id.to_def_id()).as_str().starts_with('_') {
794-
return;
795-
}
796766
self.warn_multiple_dead_codes(&[id], participle, None);
797767
}
798-
}
799768

800-
impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
801-
type NestedFilter = nested_filter::All;
802-
803-
/// Walk nested items in place so that we don't report dead-code
804-
/// on inner functions when the outer function is already getting
805-
/// an error. We could do this also by checking the parents, but
806-
/// this is how the code is setup and it seems harmless enough.
807-
fn nested_visit_map(&mut self) -> Self::Map {
808-
self.tcx.hir()
809-
}
810-
811-
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
812-
if self.should_warn_about_item(item) {
813-
// For most items, we want to highlight its identifier
814-
let participle = match item.kind {
815-
hir::ItemKind::Struct(..) => "constructed", // Issue #52325
816-
_ => "used",
817-
};
818-
self.warn_dead_code(item.def_id, participle);
819-
} else {
820-
// Only continue if we didn't warn
821-
intravisit::walk_item(self, item);
769+
fn check_definition(&mut self, def_id: LocalDefId) {
770+
if self.live_symbols.contains(&def_id) {
771+
return;
822772
}
823-
}
824-
825-
// This visitor should only visit a single module at a time.
826-
fn visit_mod(&mut self, _: &'tcx hir::Mod<'tcx>, _: Span, _: hir::HirId) {}
827-
828-
fn visit_enum_def(
829-
&mut self,
830-
enum_definition: &'tcx hir::EnumDef<'tcx>,
831-
generics: &'tcx hir::Generics<'tcx>,
832-
item_id: hir::HirId,
833-
_: Span,
834-
) {
835-
intravisit::walk_enum_def(self, enum_definition, generics, item_id);
836-
let dead_variants = enum_definition
837-
.variants
838-
.iter()
839-
.filter_map(|variant| {
840-
if self.should_warn_about_variant(&variant) {
841-
Some(DeadVariant {
842-
def_id: self.tcx.hir().local_def_id(variant.id),
843-
name: variant.ident.name,
844-
level: self.tcx.lint_level_at_node(lint::builtin::DEAD_CODE, variant.id).0,
845-
})
846-
} else {
847-
None
848-
}
849-
})
850-
.collect();
851-
self.warn_dead_fields_and_variants(item_id.expect_owner(), "constructed", dead_variants)
852-
}
853-
854-
fn visit_variant(
855-
&mut self,
856-
variant: &'tcx hir::Variant<'tcx>,
857-
g: &'tcx hir::Generics<'tcx>,
858-
id: hir::HirId,
859-
) {
860-
if !self.should_warn_about_variant(&variant) {
861-
intravisit::walk_variant(self, variant, g, id);
773+
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
774+
if has_allow_dead_code_or_lang_attr(self.tcx, hir_id) {
775+
return;
862776
}
863-
}
864-
865-
fn visit_foreign_item(&mut self, fi: &'tcx hir::ForeignItem<'tcx>) {
866-
if self.should_warn_about_foreign_item(fi) {
867-
self.warn_dead_code(fi.def_id, "used");
777+
let Some(name) = self.tcx.opt_item_name(def_id.to_def_id()) else {
778+
return
779+
};
780+
if name.as_str().starts_with('_') {
781+
return;
782+
}
783+
match self.tcx.def_kind(def_id) {
784+
DefKind::AssocConst
785+
| DefKind::AssocFn
786+
| DefKind::Fn
787+
| DefKind::Static(_)
788+
| DefKind::Const
789+
| DefKind::TyAlias
790+
| DefKind::Enum
791+
| DefKind::Union
792+
| DefKind::ForeignTy => self.warn_dead_code(def_id, "used"),
793+
DefKind::Struct => self.warn_dead_code(def_id, "constructed"),
794+
DefKind::Variant | DefKind::Field => bug!("should be handled specially"),
795+
_ => {}
868796
}
869-
intravisit::walk_foreign_item(self, fi);
870797
}
798+
}
871799

872-
fn visit_variant_data(
873-
&mut self,
874-
def: &'tcx hir::VariantData<'tcx>,
875-
_: Symbol,
876-
_: &hir::Generics<'_>,
877-
id: hir::HirId,
878-
_: rustc_span::Span,
879-
) {
880-
intravisit::walk_struct_def(self, def);
881-
let dead_fields = def
882-
.fields()
883-
.iter()
884-
.filter_map(|field| {
885-
if self.should_warn_about_field(&field) {
886-
Some(DeadVariant {
887-
def_id: self.tcx.hir().local_def_id(field.hir_id),
888-
name: field.ident.name,
889-
level: self
890-
.tcx
891-
.lint_level_at_node(lint::builtin::DEAD_CODE, field.hir_id)
892-
.0,
893-
})
894-
} else {
895-
None
896-
}
897-
})
898-
.collect();
899-
self.warn_dead_fields_and_variants(self.tcx.hir().local_def_id(id), "read", dead_fields)
900-
}
800+
fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalDefId) {
801+
let (live_symbols, ignored_derived_traits) = tcx.live_symbols_and_ignored_derived_traits(());
802+
let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits };
901803

902-
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
903-
match impl_item.kind {
904-
hir::ImplItemKind::Const(_, body_id) => {
905-
if !self.symbol_is_live(impl_item.def_id) {
906-
self.warn_dead_code(impl_item.def_id, "used");
907-
}
908-
self.visit_nested_body(body_id)
804+
let module_items = tcx.hir_module_items(module);
805+
806+
for item in module_items.items() {
807+
if !live_symbols.contains(&item.def_id) {
808+
let parent = tcx.local_parent(item.def_id);
809+
if parent != module && !live_symbols.contains(&parent) {
810+
// We already have diagnosed something.
811+
continue;
909812
}
910-
hir::ImplItemKind::Fn(_, body_id) => {
911-
if !self.symbol_is_live(impl_item.def_id) {
912-
self.warn_dead_code(impl_item.def_id, "used");
813+
visitor.check_definition(item.def_id);
814+
continue;
815+
}
816+
817+
let def_kind = tcx.def_kind(item.def_id);
818+
if let DefKind::Struct | DefKind::Union | DefKind::Enum = def_kind {
819+
let adt = tcx.adt_def(item.def_id);
820+
let mut dead_variants = Vec::new();
821+
822+
for variant in adt.variants() {
823+
let def_id = variant.def_id.expect_local();
824+
if !live_symbols.contains(&def_id) {
825+
// Record to group diagnostics.
826+
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
827+
let level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
828+
dead_variants.push(DeadVariant { def_id, name: variant.name, level });
829+
continue;
913830
}
914-
self.visit_nested_body(body_id)
831+
832+
let dead_fields = variant
833+
.fields
834+
.iter()
835+
.filter_map(|field| {
836+
let def_id = field.did.expect_local();
837+
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
838+
if visitor.should_warn_about_field(&field) {
839+
let level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
840+
Some(DeadVariant { def_id, name: field.name, level })
841+
} else {
842+
None
843+
}
844+
})
845+
.collect();
846+
visitor.warn_dead_fields_and_variants(def_id, "read", dead_fields)
915847
}
916-
hir::ImplItemKind::TyAlias(..) => {}
848+
849+
visitor.warn_dead_fields_and_variants(item.def_id, "constructed", dead_variants);
917850
}
918851
}
919852

920-
// Overwrite so that we don't warn the trait item itself.
921-
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
922-
match trait_item.kind {
923-
hir::TraitItemKind::Const(_, Some(body_id))
924-
| hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)) => {
925-
self.visit_nested_body(body_id)
926-
}
927-
hir::TraitItemKind::Const(_, None)
928-
| hir::TraitItemKind::Fn(_, hir::TraitFn::Required(_))
929-
| hir::TraitItemKind::Type(..) => {}
930-
}
853+
for impl_item in module_items.impl_items() {
854+
visitor.check_definition(impl_item.def_id);
931855
}
932-
}
933856

934-
fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalDefId) {
935-
let (live_symbols, ignored_derived_traits) = tcx.live_symbols_and_ignored_derived_traits(());
936-
let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits };
937-
let (module, _, module_id) = tcx.hir().get_module(module);
938-
// Do not use an ItemLikeVisitor since we may want to skip visiting some items
939-
// when a surrounding one is warned against or `_`.
940-
intravisit::walk_mod(&mut visitor, module, module_id);
857+
for foreign_item in module_items.foreign_items() {
858+
visitor.check_definition(foreign_item.def_id);
859+
}
860+
861+
// We do not warn trait items.
941862
}
942863

943864
pub(crate) fn provide(providers: &mut Providers) {

src/test/ui/lint/dead-code/issue-85255.stderr

+20-20
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,26 @@ note: the lint level is defined here
1414
LL | #![warn(dead_code)]
1515
| ^^^^^^^^^
1616

17+
warning: fields `a` and `b` are never read
18+
--> $DIR/issue-85255.rs:19:5
19+
|
20+
LL | pub(crate) struct Foo1 {
21+
| ---- fields in this struct
22+
LL | a: i32,
23+
| ^
24+
LL | pub b: i32,
25+
| ^
26+
27+
warning: fields `a` and `b` are never read
28+
--> $DIR/issue-85255.rs:31:5
29+
|
30+
LL | pub(crate) struct Foo2 {
31+
| ---- fields in this struct
32+
LL | a: i32,
33+
| ^
34+
LL | pub b: i32,
35+
| ^
36+
1737
warning: associated function `a` is never used
1838
--> $DIR/issue-85255.rs:14:8
1939
|
@@ -26,16 +46,6 @@ warning: associated function `b` is never used
2646
LL | pub fn b(&self) -> i32 { 6 }
2747
| ^
2848

29-
warning: fields `a` and `b` are never read
30-
--> $DIR/issue-85255.rs:19:5
31-
|
32-
LL | pub(crate) struct Foo1 {
33-
| ---- fields in this struct
34-
LL | a: i32,
35-
| ^
36-
LL | pub b: i32,
37-
| ^
38-
3949
warning: associated function `a` is never used
4050
--> $DIR/issue-85255.rs:26:8
4151
|
@@ -48,16 +58,6 @@ warning: associated function `b` is never used
4858
LL | pub fn b(&self) -> i32 { 6 }
4959
| ^
5060

51-
warning: fields `a` and `b` are never read
52-
--> $DIR/issue-85255.rs:31:5
53-
|
54-
LL | pub(crate) struct Foo2 {
55-
| ---- fields in this struct
56-
LL | a: i32,
57-
| ^
58-
LL | pub b: i32,
59-
| ^
60-
6161
warning: associated function `a` is never used
6262
--> $DIR/issue-85255.rs:38:8
6363
|

src/test/ui/lint/dead-code/lint-dead-code-3.rs

+11
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,18 @@ mod inner {
7373
fn f() {}
7474
}
7575

76+
fn anon_const() -> [(); {
77+
fn blah() {} //~ ERROR: function `blah` is never used
78+
1
79+
}] {
80+
[(); {
81+
fn blah() {} //~ ERROR: function `blah` is never used
82+
1
83+
}]
84+
}
85+
7686
pub fn foo() {
7787
let a: &dyn inner::Trait = &1_isize;
7888
a.f();
89+
anon_const();
7990
}

0 commit comments

Comments
 (0)