Skip to content

Commit

Permalink
Detect unused structs which derived Default
Browse files Browse the repository at this point in the history
  • Loading branch information
mu001999 committed Jun 12, 2024
1 parent 336e6ab commit 1eccd53
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 2 deletions.
26 changes: 26 additions & 0 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,32 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
return false;
}

// don't ignore impl Default for Enums and pub Structs
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 self_ty = self.tcx.hir().expect_item(local_impl_of).expect_impl().self_ty;

// 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
if ty_ref_to_pub_struct(self.tcx, &self_ty).ty_is_public {
return false;
}

// don't ignore impl Default for Enums,
// cause we don't know which variant is constructed
if let TyKind::Path(hir::QPath::Resolved(_, path)) = self_ty.kind
&& let Res::Def(DefKind::Enum, did) = path.res
&& did.is_local()
{
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)
{
Expand Down
2 changes: 1 addition & 1 deletion library/alloc/src/sync/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
}

Expand Down
1 change: 1 addition & 0 deletions library/core/src/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/deriving/deriving-default-enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
25 changes: 25 additions & 0 deletions tests/ui/lint/dead-code/unused-struct-derive-default.rs
Original file line number Diff line number Diff line change
@@ -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();
}
24 changes: 24 additions & 0 deletions tests/ui/lint/dead-code/unused-struct-derive-default.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 1eccd53

Please sign in to comment.