Detect unused structs which derived Default

This commit is contained in:
mu001999 2024-06-25 23:29:44 +08:00
parent 336e6ab3b3
commit 6997b6876d
7 changed files with 78 additions and 2 deletions

View File

@ -400,6 +400,31 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
return false;
}
// don't ignore impls for Enums and pub Structs whose methods don't have self receiver,
// cause external crate may call such methods to construct values of these types
if let Some(local_impl_of) = impl_of.as_local()
&& let Some(local_def_id) = def_id.as_local()
&& let Some(fn_sig) =
self.tcx.hir().fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
&& let TyKind::Path(hir::QPath::Resolved(_, path)) =
self.tcx.hir().expect_item(local_impl_of).expect_impl().self_ty.kind
&& let Res::Def(def_kind, did) = path.res
{
match def_kind {
// for example, #[derive(Default)] pub struct T(i32);
// external crate can call T::default() to construct T,
// so that don't ignore impl Default for pub Enum and Structs
DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => {
return false;
}
// don't ignore impl Default for Enums,
// cause we don't know which variant is constructed
DefKind::Enum => return false,
_ => (),
};
}
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
&& self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
{

View File

@ -396,7 +396,7 @@ fn show_arc() {
// Make sure deriving works with Arc<T>
#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug, Default)]
struct Foo {
struct _Foo {
inner: Arc<i32>,
}

View File

@ -103,6 +103,7 @@ use crate::ascii::Char as AsciiChar;
/// ```
#[cfg_attr(not(test), rustc_diagnostic_item = "Default")]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(bootstrap), rustc_trivial_field_reads)]
pub trait Default: Sized {
/// Returns the "default value" for a type.
///

View File

@ -22,6 +22,6 @@ enum MyOption<T> {
}
fn main() {
assert_eq!(Foo::default(), Foo::Alpha);
assert!(matches!(Foo::default(), Foo::Alpha));
assert!(matches!(MyOption::<NotDefault>::default(), MyOption::None));
}

View File

@ -7,6 +7,7 @@
use std::panic::catch_unwind;
#[allow(dead_code)]
#[derive(Default)]
struct Guard;

View File

@ -0,0 +1,25 @@
#![deny(dead_code)]
#[derive(Default)]
struct T; //~ ERROR struct `T` is never constructed
#[derive(Default)]
struct Used;
#[derive(Default)]
enum E {
#[default]
A,
B, //~ ERROR variant `B` is never constructed
}
// external crate can call T2::default() to construct T2,
// so that no warnings for pub adts
#[derive(Default)]
pub struct T2 {
_unread: i32,
}
fn main() {
let _x: Used = Default::default();
}

View File

@ -0,0 +1,24 @@
error: struct `T` is never constructed
--> $DIR/unused-struct-derive-default.rs:4:8
|
LL | struct T;
| ^
|
= note: `T` has a derived impl for the trait `Default`, but this is intentionally ignored during dead code analysis
note: the lint level is defined here
--> $DIR/unused-struct-derive-default.rs:1:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^
error: variant `B` is never constructed
--> $DIR/unused-struct-derive-default.rs:13:5
|
LL | enum E {
| - variant in this enum
...
LL | B,
| ^
error: aborting due to 2 previous errors