Skip to content

Commit

Permalink
Detect pub structs never constructed
Browse files Browse the repository at this point in the history
  • Loading branch information
mu001999 committed May 26, 2024
1 parent 21e6de7 commit f24ad4b
Show file tree
Hide file tree
Showing 24 changed files with 109 additions and 39 deletions.
31 changes: 26 additions & 5 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustc_hir::{Node, PatKind, TyKind};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::middle::privacy::Level;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::ty::{self, AssocItemContainer, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
use rustc_session::lint::builtin::DEAD_CODE;
Expand Down Expand Up @@ -44,16 +44,25 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
)
}

fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
tcx.adt_def(id).all_fields().all(|field| field.vis.is_public())
}

fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
&& let Res::Def(def_kind, def_id) = path.res
&& def_id.is_local()
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
{
tcx.visibility(def_id).is_public()
} else {
true
return match def_kind {
DefKind::Enum | DefKind::Union => tcx.visibility(def_id).is_public(),
DefKind::Struct => {
tcx.visibility(def_id).is_public() && struct_all_fields_are_public(tcx, def_id)
}
_ => true,
};
}

true
}

/// Determine if a work from the worklist is coming from the a `#[allow]`
Expand Down Expand Up @@ -841,6 +850,18 @@ fn create_and_seed_worklist(
effective_vis
.is_public_at_level(Level::Reachable)
.then_some(id)
.filter(|&id|
// checks impl-of-traits, impl-item-of-trait-items and pub structs with all public fields later
match tcx.def_kind(id) {
DefKind::Impl { of_trait: true } => false,
DefKind::AssocFn => {
let assoc_item = tcx.associated_item(id);
!matches!(assoc_item.container, AssocItemContainer::ImplContainer)
|| assoc_item.trait_item_def_id.is_none()
}
DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()),
_ => true
})
.map(|id| (id, ComesFromAllowExpect::No))
})
// Seed entry point
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ pub macro Clone($item:item) {
//
// These structs should never appear in user code.
#[doc(hidden)]
#[allow(dead_code)]
#[allow(missing_debug_implementations)]
#[unstable(
feature = "derive_clone_copy",
Expand All @@ -194,6 +195,7 @@ pub struct AssertParamIsClone<T: Clone + ?Sized> {
_field: crate::marker::PhantomData<T>,
}
#[doc(hidden)]
#[allow(dead_code)]
#[allow(missing_debug_implementations)]
#[unstable(
feature = "derive_clone_copy",
Expand Down
1 change: 1 addition & 0 deletions tests/ui/coherence/re-rebalance-coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
extern crate re_rebalance_coherence_lib as lib;
use lib::*;

#[allow(dead_code)]
struct Oracle;
impl Backend for Oracle {}
impl<'a, T:'a, Tab> QueryFragment<Oracle> for BatchInsert<'a, T, Tab> {}
Expand Down
1 change: 1 addition & 0 deletions tests/ui/const-generics/defaults/repr-c-issue-82792.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

//@ run-pass

#[allow(dead_code)]
#[repr(C)]
pub struct Loaf<T: Sized, const N: usize = 1> {
head: [T; N],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ impl BlockCipher for BarCipher {
const BLOCK_SIZE: usize = 32;
}

pub struct Block<C>(#[allow(dead_code)] C);
#[allow(dead_code)]
pub struct Block<C>( C);

pub fn test<C: BlockCipher, const M: usize>()
where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use std::mem::MaybeUninit;

#[allow(dead_code)]
#[repr(transparent)]
pub struct MaybeUninitWrapper<const N: usize>(MaybeUninit<[u64; N]>);

Expand Down
4 changes: 2 additions & 2 deletions tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#![forbid(dead_code)]

#[derive(Debug)]
pub struct Whatever {
pub struct Whatever { //~ ERROR struct `Whatever` is never constructed
pub field0: (),
field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read
field1: (),
field2: (),
field3: (),
field4: (),
Expand Down
16 changes: 3 additions & 13 deletions tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
error: fields `field1`, `field2`, `field3`, and `field4` are never read
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5
error: struct `Whatever` is never constructed
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:4:12
|
LL | pub struct Whatever {
| -------- fields in this struct
LL | pub field0: (),
LL | field1: (),
| ^^^^^^
LL | field2: (),
| ^^^^^^
LL | field3: (),
| ^^^^^^
LL | field4: (),
| ^^^^^^
| ^^^^^^^^
|
= note: `Whatever` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
note: the lint level is defined here
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:1:11
|
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/lint/dead-code/lint-dead-code-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ struct SemiUsedStruct;
impl SemiUsedStruct {
fn la_la_la() {}
}
struct StructUsedAsField;
struct StructUsedAsField; //~ ERROR struct `StructUsedAsField` is never constructed
pub struct StructUsedInEnum;
struct StructUsedInGeneric;
pub struct PubStruct2 {
pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed
#[allow(dead_code)]
struct_used_as_field: *const StructUsedAsField
}
Expand Down
14 changes: 13 additions & 1 deletion tests/ui/lint/dead-code/lint-dead-code-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ error: struct `PrivStruct` is never constructed
LL | struct PrivStruct;
| ^^^^^^^^^^

error: struct `StructUsedAsField` is never constructed
--> $DIR/lint-dead-code-1.rs:49:8
|
LL | struct StructUsedAsField;
| ^^^^^^^^^^^^^^^^^

error: struct `PubStruct2` is never constructed
--> $DIR/lint-dead-code-1.rs:52:12
|
LL | pub struct PubStruct2 {
| ^^^^^^^^^^

error: enum `priv_enum` is never used
--> $DIR/lint-dead-code-1.rs:64:6
|
Expand Down Expand Up @@ -67,5 +79,5 @@ error: struct `Bar` is never constructed
LL | pub struct Bar;
| ^^^

error: aborting due to 10 previous errors
error: aborting due to 12 previous errors

17 changes: 17 additions & 0 deletions tests/ui/lint/dead-code/unused_pub_struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#![deny(dead_code)]

pub struct T(()); //~ ERROR struct `T` is never constructed

impl Clone for T {
fn clone(&self) -> T {
T(())
}
}

pub trait Trait {
fn foo(&self) {}
}

impl Trait for T {}

fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/lint/dead-code/unused_pub_struct.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: struct `T` is never constructed
--> $DIR/unused_pub_struct.rs:3:12
|
LL | pub struct T(());
| ^
|
note: the lint level is defined here
--> $DIR/unused_pub_struct.rs:1:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Regression test for issues #100790 and #106439.
//@ run-rustfix

pub struct Example(#[allow(dead_code)] usize)
#[allow(dead_code)]
pub struct Example(usize)
where
(): Sized;
//~^^^ ERROR where clauses are not allowed before tuple struct bodies
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// Regression test for issues #100790 and #106439.
//@ run-rustfix

#[allow(dead_code)]
pub struct Example
where
(): Sized,
(#[allow(dead_code)] usize);
(usize);
//~^^^ ERROR where clauses are not allowed before tuple struct bodies

struct _Demo
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
error: where clauses are not allowed before tuple struct bodies
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1
|
LL | pub struct Example
| ------- while parsing this tuple struct
LL | / where
LL | | (): Sized,
| |______________^ unexpected where clause
LL | (#[allow(dead_code)] usize);
| --------------------------- the struct body
LL | (usize);
| ------- the struct body
|
help: move the body before the where clause
|
LL ~ pub struct Example(#[allow(dead_code)] usize)
LL ~ pub struct Example(usize)
LL | where
LL ~ (): Sized;
|

error: where clauses are not allowed before tuple struct bodies
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1
|
LL | struct _Demo
| ----- while parsing this tuple struct
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/pub/pub-ident-struct-4.fixed
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ run-rustfix

pub struct T(#[allow(dead_code)] String);
#[allow(dead_code)]
pub struct T(String);
//~^ ERROR missing `struct` for struct definition

fn main() {}
3 changes: 2 additions & 1 deletion tests/ui/pub/pub-ident-struct-4.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ run-rustfix

pub T(#[allow(dead_code)] String);
#[allow(dead_code)]
pub T(String);
//~^ ERROR missing `struct` for struct definition

fn main() {}
6 changes: 3 additions & 3 deletions tests/ui/pub/pub-ident-struct-4.stderr
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
error: missing `struct` for struct definition
--> $DIR/pub-ident-struct-4.rs:3:4
--> $DIR/pub-ident-struct-4.rs:4:4
|
LL | pub T(#[allow(dead_code)] String);
LL | pub T(String);
| ^
|
help: add `struct` here to parse `T` as a public struct
|
LL | pub struct T(#[allow(dead_code)] String);
LL | pub struct T(String);
| ++++++

error: aborting due to 1 previous error
Expand Down
1 change: 1 addition & 0 deletions tests/ui/regions/regions-issue-21422.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

//@ pretty-expanded FIXME #23616

#[allow(dead_code)]
pub struct P<'a> {
_ptr: *const &'a u8,
}
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/structs-enums/newtype-struct-with-dtor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
#![allow(unused_variables)]
//@ pretty-expanded FIXME #23616

#[allow(dead_code)]
pub struct Fd(u32);

#[allow(dead_code)]
fn foo(a: u32) {}

impl Drop for Fd {
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/structs-enums/uninstantiable-struct.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//@ run-pass
pub struct Z(#[allow(dead_code)] &'static Z);
#[allow(dead_code)]
pub struct Z(&'static Z);

pub fn main() {}
1 change: 1 addition & 0 deletions tests/ui/suggestions/derive-clone-for-eq.fixed
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ run-rustfix
// https://github.com/rust-lang/rust/issues/79076

#[allow(dead_code)]
#[derive(Clone, Eq)] //~ ERROR [E0277]
pub struct Struct<T: std::clone::Clone>(T);

Expand Down
1 change: 1 addition & 0 deletions tests/ui/suggestions/derive-clone-for-eq.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ run-rustfix
// https://github.com/rust-lang/rust/issues/79076

#[allow(dead_code)]
#[derive(Clone, Eq)] //~ ERROR [E0277]
pub struct Struct<T>(T);

Expand Down
4 changes: 2 additions & 2 deletions tests/ui/suggestions/derive-clone-for-eq.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error[E0277]: the trait bound `T: Clone` is not satisfied
--> $DIR/derive-clone-for-eq.rs:4:17
--> $DIR/derive-clone-for-eq.rs:5:17
|
LL | #[derive(Clone, Eq)]
| ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct<T>: PartialEq`
|
note: required for `Struct<T>` to implement `PartialEq`
--> $DIR/derive-clone-for-eq.rs:7:19
--> $DIR/derive-clone-for-eq.rs:8:19
|
LL | impl<T: Clone, U> PartialEq<U> for Struct<T>
| ----- ^^^^^^^^^^^^ ^^^^^^^^^
Expand Down

0 comments on commit f24ad4b

Please sign in to comment.