Skip to content

Commit 9a82f74

Browse files
authored
Rollup merge of #92783 - FabianWolff:issue-92726, r=nikomatsakis
Annotate dead code lint with notes about ignored derived impls Fixes #92726. CC `@pmetzger,` is this what you had in mind? r? `@nikomatsakis`
2 parents 3148a32 + 8b459dd commit 9a82f74

File tree

4 files changed

+87
-6
lines changed

4 files changed

+87
-6
lines changed

compiler/rustc_passes/src/dead.rs

+54-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// from live codes are live, and everything else is dead.
44

55
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
6+
use rustc_errors::pluralize;
67
use rustc_hir as hir;
78
use rustc_hir::def::{CtorOf, DefKind, Res};
89
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -15,6 +16,7 @@ use rustc_middle::middle::privacy;
1516
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
1617
use rustc_session::lint;
1718
use rustc_span::symbol::{sym, Symbol};
19+
use rustc_span::Span;
1820
use std::mem;
1921

2022
// Any local node that may call something in its body block should be
@@ -47,6 +49,10 @@ struct MarkSymbolVisitor<'tcx> {
4749
ignore_variant_stack: Vec<DefId>,
4850
// maps from tuple struct constructors to tuple struct items
4951
struct_constructors: FxHashMap<LocalDefId, LocalDefId>,
52+
// maps from ADTs to ignored derived traits (e.g. Debug and Clone)
53+
// and the span of their respective impl (i.e., part of the derive
54+
// macro)
55+
ignored_derived_traits: FxHashMap<DefId, Vec<(Span, DefId)>>,
5056
}
5157

5258
impl<'tcx> MarkSymbolVisitor<'tcx> {
@@ -242,14 +248,24 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
242248
/// Automatically generated items marked with `rustc_trivial_field_reads`
243249
/// will be ignored for the purposes of dead code analysis (see PR #85200
244250
/// for discussion).
245-
fn should_ignore_item(&self, def_id: DefId) -> bool {
251+
fn should_ignore_item(&mut self, def_id: DefId) -> bool {
246252
if let Some(impl_of) = self.tcx.impl_of_method(def_id) {
247253
if !self.tcx.has_attr(impl_of, sym::automatically_derived) {
248254
return false;
249255
}
250256

251257
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) {
252258
if self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) {
259+
let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap();
260+
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 {
265+
self.ignored_derived_traits
266+
.insert(adt_def.did, vec![(impl_span, trait_of)]);
267+
}
268+
}
253269
return true;
254270
}
255271
}
@@ -571,7 +587,7 @@ fn create_and_seed_worklist<'tcx>(
571587
fn find_live<'tcx>(
572588
tcx: TyCtxt<'tcx>,
573589
access_levels: &privacy::AccessLevels,
574-
) -> FxHashSet<LocalDefId> {
590+
) -> (FxHashSet<LocalDefId>, FxHashMap<DefId, Vec<(Span, DefId)>>) {
575591
let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels);
576592
let mut symbol_visitor = MarkSymbolVisitor {
577593
worklist,
@@ -584,14 +600,16 @@ fn find_live<'tcx>(
584600
pub_visibility: false,
585601
ignore_variant_stack: vec![],
586602
struct_constructors,
603+
ignored_derived_traits: FxHashMap::default(),
587604
};
588605
symbol_visitor.mark_live_symbols();
589-
symbol_visitor.live_symbols
606+
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
590607
}
591608

592609
struct DeadVisitor<'tcx> {
593610
tcx: TyCtxt<'tcx>,
594611
live_symbols: FxHashSet<LocalDefId>,
612+
ignored_derived_traits: FxHashMap<DefId, Vec<(Span, DefId)>>,
595613
}
596614

597615
impl<'tcx> DeadVisitor<'tcx> {
@@ -660,7 +678,37 @@ impl<'tcx> DeadVisitor<'tcx> {
660678
self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| {
661679
let def_id = self.tcx.hir().local_def_id(id);
662680
let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
663-
lint.build(&format!("{} is never {}: `{}`", descr, participle, name)).emit()
681+
let mut err = lint.build(&format!("{} is never {}: `{}`", descr, participle, name));
682+
let hir = self.tcx.hir();
683+
if let Some(encl_scope) = hir.get_enclosing_scope(id) {
684+
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+
{
688+
let traits_str = ign_traits
689+
.iter()
690+
.map(|(_, t)| format!("`{}`", self.tcx.item_name(*t)))
691+
.collect::<Vec<_>>()
692+
.join(" and ");
693+
let plural_s = pluralize!(ign_traits.len());
694+
let article = if ign_traits.len() > 1 { "" } else { "a " };
695+
let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" };
696+
let msg = format!(
697+
"`{}` has {}derived impl{} for the trait{} {}, but {} \
698+
intentionally ignored during dead code analysis",
699+
self.tcx.item_name(encl_def_id.to_def_id()),
700+
article,
701+
plural_s,
702+
plural_s,
703+
traits_str,
704+
is_are
705+
);
706+
let multispan = ign_traits.iter().map(|(s, _)| *s).collect::<Vec<_>>();
707+
err.span_note(multispan, &msg);
708+
}
709+
}
710+
}
711+
err.emit();
664712
});
665713
}
666714
}
@@ -790,7 +838,7 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
790838

791839
pub fn check_crate(tcx: TyCtxt<'_>) {
792840
let access_levels = &tcx.privacy_access_levels(());
793-
let live_symbols = find_live(tcx, access_levels);
794-
let mut visitor = DeadVisitor { tcx, live_symbols };
841+
let (live_symbols, ignored_derived_traits) = find_live(tcx, access_levels);
842+
let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits };
795843
tcx.hir().walk_toplevel_module(&mut visitor);
796844
}

src/test/ui/derive-uninhabited-enum-38885.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ LL | Void(Void),
55
| ^^^^^^^^^^
66
|
77
= note: `-W dead-code` implied by `-W unused`
8+
note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
9+
--> $DIR/derive-uninhabited-enum-38885.rs:10:10
10+
|
11+
LL | #[derive(Debug)]
12+
| ^^^^^
13+
= note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
814

915
warning: 1 warning emitted
1016

src/test/ui/derives/clone-debug-dead-code.stderr

+21
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,39 @@ error: field is never read: `f`
1515
|
1616
LL | struct B { f: () }
1717
| ^^^^^
18+
|
19+
note: `B` has a derived impl for the trait `Clone`, but this is intentionally ignored during dead code analysis
20+
--> $DIR/clone-debug-dead-code.rs:9:10
21+
|
22+
LL | #[derive(Clone)]
23+
| ^^^^^
24+
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
1825

1926
error: field is never read: `f`
2027
--> $DIR/clone-debug-dead-code.rs:14:12
2128
|
2229
LL | struct C { f: () }
2330
| ^^^^^
31+
|
32+
note: `C` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
33+
--> $DIR/clone-debug-dead-code.rs:13:10
34+
|
35+
LL | #[derive(Debug)]
36+
| ^^^^^
37+
= note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
2438

2539
error: field is never read: `f`
2640
--> $DIR/clone-debug-dead-code.rs:18:12
2741
|
2842
LL | struct D { f: () }
2943
| ^^^^^
44+
|
45+
note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
46+
--> $DIR/clone-debug-dead-code.rs:17:10
47+
|
48+
LL | #[derive(Debug,Clone)]
49+
| ^^^^^ ^^^^^
50+
= note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
3051

3152
error: field is never read: `f`
3253
--> $DIR/clone-debug-dead-code.rs:21:12

src/test/ui/lint/dead-code/unused-variant.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ note: the lint level is defined here
99
|
1010
LL | #![deny(dead_code)]
1111
| ^^^^^^^^^
12+
note: `Enum` has a derived impl for the trait `Clone`, but this is intentionally ignored during dead code analysis
13+
--> $DIR/unused-variant.rs:3:10
14+
|
15+
LL | #[derive(Clone)]
16+
| ^^^^^
17+
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
1218

1319
error: aborting due to previous error
1420

0 commit comments

Comments
 (0)