mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Annotate dead code lint with notes about ignored derived impls
This commit is contained in:
parent
2e2c86eba2
commit
bd1f09d417
@ -3,6 +3,7 @@
|
|||||||
// from live codes are live, and everything else is dead.
|
// from live codes are live, and everything else is dead.
|
||||||
|
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
|
use rustc_errors::pluralize;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
@ -47,6 +48,8 @@ struct MarkSymbolVisitor<'tcx> {
|
|||||||
ignore_variant_stack: Vec<DefId>,
|
ignore_variant_stack: Vec<DefId>,
|
||||||
// maps from tuple struct constructors to tuple struct items
|
// maps from tuple struct constructors to tuple struct items
|
||||||
struct_constructors: FxHashMap<LocalDefId, LocalDefId>,
|
struct_constructors: FxHashMap<LocalDefId, LocalDefId>,
|
||||||
|
// maps from ADTs to ignored derived traits (e.g. Debug and Clone)
|
||||||
|
ignored_derived_traits: FxHashMap<DefId, Vec<DefId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> MarkSymbolVisitor<'tcx> {
|
impl<'tcx> MarkSymbolVisitor<'tcx> {
|
||||||
@ -242,7 +245,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||||||
/// Automatically generated items marked with `rustc_trivial_field_reads`
|
/// Automatically generated items marked with `rustc_trivial_field_reads`
|
||||||
/// will be ignored for the purposes of dead code analysis (see PR #85200
|
/// will be ignored for the purposes of dead code analysis (see PR #85200
|
||||||
/// for discussion).
|
/// for discussion).
|
||||||
fn should_ignore_item(&self, def_id: DefId) -> bool {
|
fn should_ignore_item(&mut self, def_id: DefId) -> bool {
|
||||||
if let Some(impl_of) = self.tcx.impl_of_method(def_id) {
|
if let Some(impl_of) = self.tcx.impl_of_method(def_id) {
|
||||||
if !self.tcx.has_attr(impl_of, sym::automatically_derived) {
|
if !self.tcx.has_attr(impl_of, sym::automatically_derived) {
|
||||||
return false;
|
return false;
|
||||||
@ -250,6 +253,14 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||||||
|
|
||||||
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) {
|
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) {
|
||||||
if self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) {
|
if self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) {
|
||||||
|
let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap();
|
||||||
|
if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() {
|
||||||
|
if let Some(v) = self.ignored_derived_traits.get_mut(&adt_def.did) {
|
||||||
|
v.push(trait_of);
|
||||||
|
} else {
|
||||||
|
self.ignored_derived_traits.insert(adt_def.did, vec![trait_of]);
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -577,7 +588,7 @@ fn create_and_seed_worklist<'tcx>(
|
|||||||
fn find_live<'tcx>(
|
fn find_live<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
access_levels: &privacy::AccessLevels,
|
access_levels: &privacy::AccessLevels,
|
||||||
) -> FxHashSet<LocalDefId> {
|
) -> (FxHashSet<LocalDefId>, FxHashMap<DefId, Vec<DefId>>) {
|
||||||
let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels);
|
let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels);
|
||||||
let mut symbol_visitor = MarkSymbolVisitor {
|
let mut symbol_visitor = MarkSymbolVisitor {
|
||||||
worklist,
|
worklist,
|
||||||
@ -590,14 +601,16 @@ fn find_live<'tcx>(
|
|||||||
pub_visibility: false,
|
pub_visibility: false,
|
||||||
ignore_variant_stack: vec![],
|
ignore_variant_stack: vec![],
|
||||||
struct_constructors,
|
struct_constructors,
|
||||||
|
ignored_derived_traits: FxHashMap::default(),
|
||||||
};
|
};
|
||||||
symbol_visitor.mark_live_symbols();
|
symbol_visitor.mark_live_symbols();
|
||||||
symbol_visitor.live_symbols
|
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DeadVisitor<'tcx> {
|
struct DeadVisitor<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
live_symbols: FxHashSet<LocalDefId>,
|
live_symbols: FxHashSet<LocalDefId>,
|
||||||
|
ignored_derived_traits: FxHashMap<DefId, Vec<DefId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> DeadVisitor<'tcx> {
|
impl<'tcx> DeadVisitor<'tcx> {
|
||||||
@ -666,7 +679,34 @@ impl<'tcx> DeadVisitor<'tcx> {
|
|||||||
self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| {
|
self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| {
|
||||||
let def_id = self.tcx.hir().local_def_id(id);
|
let def_id = self.tcx.hir().local_def_id(id);
|
||||||
let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
|
let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
|
||||||
lint.build(&format!("{} is never {}: `{}`", descr, participle, name)).emit()
|
let mut err = lint.build(&format!("{} is never {}: `{}`", descr, participle, name));
|
||||||
|
let hir = self.tcx.hir();
|
||||||
|
if let Some(encl_scope) = hir.get_enclosing_scope(id) {
|
||||||
|
if let Some(encl_def_id) = hir.opt_local_def_id(encl_scope) {
|
||||||
|
if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id.to_def_id()) {
|
||||||
|
let traits_str = ign_traits
|
||||||
|
.iter()
|
||||||
|
.map(|t| format!("`{}`", self.tcx.item_name(*t))).collect::<Vec<_>>()
|
||||||
|
.join(" and ");
|
||||||
|
let plural_s = pluralize!(ign_traits.len());
|
||||||
|
let article = if ign_traits.len() > 1 { "" } else { "a " };
|
||||||
|
let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" };
|
||||||
|
let msg = format!("`{}` has {}derived impl{} for the trait{} {}, but {} ignored during dead code analysis",
|
||||||
|
self.tcx.item_name(encl_def_id.to_def_id()),
|
||||||
|
article,
|
||||||
|
plural_s,
|
||||||
|
plural_s,
|
||||||
|
traits_str,
|
||||||
|
is_are);
|
||||||
|
if let Some(span) = self.tcx.def_ident_span(encl_def_id) {
|
||||||
|
err.span_note(span, &msg);
|
||||||
|
} else {
|
||||||
|
err.note(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err.emit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -796,7 +836,7 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
|
|||||||
|
|
||||||
pub fn check_crate(tcx: TyCtxt<'_>) {
|
pub fn check_crate(tcx: TyCtxt<'_>) {
|
||||||
let access_levels = &tcx.privacy_access_levels(());
|
let access_levels = &tcx.privacy_access_levels(());
|
||||||
let live_symbols = find_live(tcx, access_levels);
|
let (live_symbols, ignored_derived_traits) = find_live(tcx, access_levels);
|
||||||
let mut visitor = DeadVisitor { tcx, live_symbols };
|
let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits };
|
||||||
tcx.hir().walk_toplevel_module(&mut visitor);
|
tcx.hir().walk_toplevel_module(&mut visitor);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,11 @@ LL | Void(Void),
|
|||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `-W dead-code` implied by `-W unused`
|
= note: `-W dead-code` implied by `-W unused`
|
||||||
|
note: `Foo` has a derived impl for the trait `Debug`, but this is ignored during dead code analysis
|
||||||
|
--> $DIR/derive-uninhabited-enum-38885.rs:11:6
|
||||||
|
|
|
||||||
|
LL | enum Foo {
|
||||||
|
| ^^^
|
||||||
|
|
||||||
warning: 1 warning emitted
|
warning: 1 warning emitted
|
||||||
|
|
||||||
|
@ -15,18 +15,36 @@ error: field is never read: `f`
|
|||||||
|
|
|
|
||||||
LL | struct B { f: () }
|
LL | struct B { f: () }
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
|
||||||
|
note: `B` has a derived impl for the trait `Clone`, but this is ignored during dead code analysis
|
||||||
|
--> $DIR/clone-debug-dead-code.rs:10:8
|
||||||
|
|
|
||||||
|
LL | struct B { f: () }
|
||||||
|
| ^
|
||||||
|
|
||||||
error: field is never read: `f`
|
error: field is never read: `f`
|
||||||
--> $DIR/clone-debug-dead-code.rs:14:12
|
--> $DIR/clone-debug-dead-code.rs:14:12
|
||||||
|
|
|
|
||||||
LL | struct C { f: () }
|
LL | struct C { f: () }
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
|
||||||
|
note: `C` has a derived impl for the trait `Debug`, but this is ignored during dead code analysis
|
||||||
|
--> $DIR/clone-debug-dead-code.rs:14:8
|
||||||
|
|
|
||||||
|
LL | struct C { f: () }
|
||||||
|
| ^
|
||||||
|
|
||||||
error: field is never read: `f`
|
error: field is never read: `f`
|
||||||
--> $DIR/clone-debug-dead-code.rs:18:12
|
--> $DIR/clone-debug-dead-code.rs:18:12
|
||||||
|
|
|
|
||||||
LL | struct D { f: () }
|
LL | struct D { f: () }
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
|
||||||
|
note: `D` has derived impls for the traits `Clone` and `Debug`, but these are ignored during dead code analysis
|
||||||
|
--> $DIR/clone-debug-dead-code.rs:18:8
|
||||||
|
|
|
||||||
|
LL | struct D { f: () }
|
||||||
|
| ^
|
||||||
|
|
||||||
error: field is never read: `f`
|
error: field is never read: `f`
|
||||||
--> $DIR/clone-debug-dead-code.rs:21:12
|
--> $DIR/clone-debug-dead-code.rs:21:12
|
||||||
|
@ -9,6 +9,11 @@ note: the lint level is defined here
|
|||||||
|
|
|
|
||||||
LL | #![deny(dead_code)]
|
LL | #![deny(dead_code)]
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
|
note: `Enum` has a derived impl for the trait `Clone`, but this is ignored during dead code analysis
|
||||||
|
--> $DIR/unused-variant.rs:4:6
|
||||||
|
|
|
||||||
|
LL | enum Enum {
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user