Skip to content

Commit d5f9c40

Browse files
committed
Auto merge of #93466 - cjgillot:query-dead, r=nagisa
Make dead code check a query. Dead code check is run for each invocation of the compiler, even if no modifications were involved. This PR makes dead code check a query keyed on the module. This allows to skip the check when a module has not changed. To perform this, a query `live_symbols_and_ignored_derived_traits` is introduced to encapsulate the global analysis of finding live symbols. The second query `check_mod_deathness` outputs diagnostics for each module based on this first query's results.
2 parents 1ea4851 + 4e7d47b commit d5f9c40

File tree

5 files changed

+62
-33
lines changed

5 files changed

+62
-33
lines changed

compiler/rustc_interface/src/passes.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,8 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
999999
tcx.ensure().check_private_in_public(());
10001000
},
10011001
{
1002-
sess.time("death_checking", || rustc_passes::dead::check_crate(tcx));
1002+
tcx.hir()
1003+
.par_for_each_module(|module| tcx.ensure().check_mod_deathness(module));
10031004
},
10041005
{
10051006
sess.time("unused_lib_feature_checking", || {

compiler/rustc_middle/src/query/mod.rs

+16
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,22 @@ rustc_queries! {
758758
desc { |tcx| "checking liveness of variables in {}", describe_as_module(key, tcx) }
759759
}
760760

761+
/// Return the live symbols in the crate for dead code check.
762+
///
763+
/// The second return value maps from ADTs to ignored derived traits (e.g. Debug and Clone) and
764+
/// their respective impl (i.e., part of the derive macro)
765+
query live_symbols_and_ignored_derived_traits(_: ()) -> (
766+
FxHashSet<LocalDefId>,
767+
FxHashMap<LocalDefId, Vec<(DefId, DefId)>>
768+
) {
769+
storage(ArenaCacheSelector<'tcx>)
770+
desc { "find live symbols in crate" }
771+
}
772+
773+
query check_mod_deathness(key: LocalDefId) -> () {
774+
desc { |tcx| "checking deathness of variables in {}", describe_as_module(key, tcx) }
775+
}
776+
761777
query check_mod_impl_wf(key: LocalDefId) -> () {
762778
desc { |tcx| "checking that impls are well-formed in {}", describe_as_module(key, tcx) }
763779
}

compiler/rustc_passes/src/dead.rs

+33-22
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_hir::{Node, PatKind, TyKind};
1313
use rustc_middle::hir::nested_filter;
1414
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1515
use rustc_middle::middle::privacy;
16+
use rustc_middle::ty::query::Providers;
1617
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
1718
use rustc_session::lint;
1819
use rustc_span::symbol::{sym, Symbol};
@@ -52,7 +53,7 @@ struct MarkSymbolVisitor<'tcx> {
5253
// maps from ADTs to ignored derived traits (e.g. Debug and Clone)
5354
// and the span of their respective impl (i.e., part of the derive
5455
// macro)
55-
ignored_derived_traits: FxHashMap<DefId, Vec<(Span, DefId)>>,
56+
ignored_derived_traits: FxHashMap<LocalDefId, Vec<(DefId, DefId)>>,
5657
}
5758

5859
impl<'tcx> MarkSymbolVisitor<'tcx> {
@@ -258,12 +259,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
258259
if self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) {
259260
let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap();
260261
if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() {
261-
let impl_span = self.tcx.def_span(impl_of);
262-
if let Some(v) = self.ignored_derived_traits.get_mut(&adt_def.did) {
263-
v.push((impl_span, trait_of));
264-
} else {
262+
if let Some(adt_def_id) = adt_def.did.as_local() {
265263
self.ignored_derived_traits
266-
.insert(adt_def.did, vec![(impl_span, trait_of)]);
264+
.entry(adt_def_id)
265+
.or_default()
266+
.push((trait_of, impl_of));
267267
}
268268
}
269269
return true;
@@ -563,8 +563,8 @@ impl<'v, 'tcx> ItemLikeVisitor<'v> for LifeSeeder<'tcx> {
563563

564564
fn create_and_seed_worklist<'tcx>(
565565
tcx: TyCtxt<'tcx>,
566-
access_levels: &privacy::AccessLevels,
567566
) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) {
567+
let access_levels = &tcx.privacy_access_levels(());
568568
let worklist = access_levels
569569
.map
570570
.iter()
@@ -584,11 +584,11 @@ fn create_and_seed_worklist<'tcx>(
584584
(life_seeder.worklist, life_seeder.struct_constructors)
585585
}
586586

587-
fn find_live<'tcx>(
587+
fn live_symbols_and_ignored_derived_traits<'tcx>(
588588
tcx: TyCtxt<'tcx>,
589-
access_levels: &privacy::AccessLevels,
590-
) -> (FxHashSet<LocalDefId>, FxHashMap<DefId, Vec<(Span, DefId)>>) {
591-
let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels);
589+
(): (),
590+
) -> (FxHashSet<LocalDefId>, FxHashMap<LocalDefId, Vec<(DefId, DefId)>>) {
591+
let (worklist, struct_constructors) = create_and_seed_worklist(tcx);
592592
let mut symbol_visitor = MarkSymbolVisitor {
593593
worklist,
594594
tcx,
@@ -608,8 +608,8 @@ fn find_live<'tcx>(
608608

609609
struct DeadVisitor<'tcx> {
610610
tcx: TyCtxt<'tcx>,
611-
live_symbols: FxHashSet<LocalDefId>,
612-
ignored_derived_traits: FxHashMap<DefId, Vec<(Span, DefId)>>,
611+
live_symbols: &'tcx FxHashSet<LocalDefId>,
612+
ignored_derived_traits: &'tcx FxHashMap<LocalDefId, Vec<(DefId, DefId)>>,
613613
}
614614

615615
impl<'tcx> DeadVisitor<'tcx> {
@@ -682,12 +682,10 @@ impl<'tcx> DeadVisitor<'tcx> {
682682
let hir = self.tcx.hir();
683683
if let Some(encl_scope) = hir.get_enclosing_scope(id) {
684684
if let Some(encl_def_id) = hir.opt_local_def_id(encl_scope) {
685-
if let Some(ign_traits) =
686-
self.ignored_derived_traits.get(&encl_def_id.to_def_id())
687-
{
685+
if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) {
688686
let traits_str = ign_traits
689687
.iter()
690-
.map(|(_, t)| format!("`{}`", self.tcx.item_name(*t)))
688+
.map(|(trait_id, _)| format!("`{}`", self.tcx.item_name(*trait_id)))
691689
.collect::<Vec<_>>()
692690
.join(" and ");
693691
let plural_s = pluralize!(ign_traits.len());
@@ -703,7 +701,10 @@ impl<'tcx> DeadVisitor<'tcx> {
703701
traits_str,
704702
is_are
705703
);
706-
let multispan = ign_traits.iter().map(|(s, _)| *s).collect::<Vec<_>>();
704+
let multispan = ign_traits
705+
.iter()
706+
.map(|(_, impl_id)| self.tcx.def_span(*impl_id))
707+
.collect::<Vec<_>>();
707708
err.span_note(multispan, &msg);
708709
}
709710
}
@@ -761,6 +762,9 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
761762
}
762763
}
763764

765+
// This visitor should only visit a single module at a time.
766+
fn visit_mod(&mut self, _: &'tcx hir::Mod<'tcx>, _: Span, _: hir::HirId) {}
767+
764768
fn visit_variant(
765769
&mut self,
766770
variant: &'tcx hir::Variant<'tcx>,
@@ -836,9 +840,16 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
836840
}
837841
}
838842

839-
pub fn check_crate(tcx: TyCtxt<'_>) {
840-
let access_levels = &tcx.privacy_access_levels(());
841-
let (live_symbols, ignored_derived_traits) = find_live(tcx, access_levels);
843+
fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalDefId) {
844+
let (live_symbols, ignored_derived_traits) = tcx.live_symbols_and_ignored_derived_traits(());
842845
let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits };
843-
tcx.hir().walk_toplevel_module(&mut visitor);
846+
let (module, _, module_id) = tcx.hir().get_module(module);
847+
// Do not use an ItemLikeVisitor since we may want to skip visiting some items
848+
// when a surrounding one is warned against or `_`.
849+
intravisit::walk_mod(&mut visitor, module, module_id);
850+
}
851+
852+
pub(crate) fn provide(providers: &mut Providers) {
853+
*providers =
854+
Providers { live_symbols_and_ignored_derived_traits, check_mod_deathness, ..*providers };
844855
}

compiler/rustc_passes/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ mod weak_lang_items;
4545
pub fn provide(providers: &mut Providers) {
4646
check_attr::provide(providers);
4747
check_const::provide(providers);
48+
dead::provide(providers);
4849
diagnostic_items::provide(providers);
4950
entry::provide(providers);
5051
lang_items::provide(providers);

src/test/ui/lint/dead-code/lint-dead-code-1.stderr

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
1-
error: struct is never constructed: `Bar`
2-
--> $DIR/lint-dead-code-1.rs:12:16
1+
error: static is never used: `priv_static`
2+
--> $DIR/lint-dead-code-1.rs:20:1
33
|
4-
LL | pub struct Bar;
5-
| ^^^
4+
LL | static priv_static: isize = 0;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
note: the lint level is defined here
88
--> $DIR/lint-dead-code-1.rs:5:9
99
|
1010
LL | #![deny(dead_code)]
1111
| ^^^^^^^^^
1212

13-
error: static is never used: `priv_static`
14-
--> $DIR/lint-dead-code-1.rs:20:1
15-
|
16-
LL | static priv_static: isize = 0;
17-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18-
1913
error: constant is never used: `priv_const`
2014
--> $DIR/lint-dead-code-1.rs:27:1
2115
|
@@ -64,5 +58,11 @@ error: function is never used: `baz`
6458
LL | fn baz() -> impl Copy {
6559
| ^^^
6660

61+
error: struct is never constructed: `Bar`
62+
--> $DIR/lint-dead-code-1.rs:12:16
63+
|
64+
LL | pub struct Bar;
65+
| ^^^
66+
6767
error: aborting due to 10 previous errors
6868

0 commit comments

Comments
 (0)