Skip to content

Commit f24ad4b

Browse files
committed
Detect pub structs never constructed
1 parent 21e6de7 commit f24ad4b

24 files changed

+109
-39
lines changed

compiler/rustc_passes/src/dead.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_hir::{Node, PatKind, TyKind};
1515
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1616
use rustc_middle::middle::privacy::Level;
1717
use rustc_middle::query::Providers;
18-
use rustc_middle::ty::{self, TyCtxt};
18+
use rustc_middle::ty::{self, AssocItemContainer, TyCtxt};
1919
use rustc_middle::{bug, span_bug};
2020
use rustc_session::lint;
2121
use rustc_session::lint::builtin::DEAD_CODE;
@@ -44,16 +44,25 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
4444
)
4545
}
4646

47+
fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
48+
tcx.adt_def(id).all_fields().all(|field| field.vis.is_public())
49+
}
50+
4751
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
4852
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
4953
&& let Res::Def(def_kind, def_id) = path.res
5054
&& def_id.is_local()
51-
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
5255
{
53-
tcx.visibility(def_id).is_public()
54-
} else {
55-
true
56+
return match def_kind {
57+
DefKind::Enum | DefKind::Union => tcx.visibility(def_id).is_public(),
58+
DefKind::Struct => {
59+
tcx.visibility(def_id).is_public() && struct_all_fields_are_public(tcx, def_id)
60+
}
61+
_ => true,
62+
};
5663
}
64+
65+
true
5766
}
5867

5968
/// Determine if a work from the worklist is coming from the a `#[allow]`
@@ -841,6 +850,18 @@ fn create_and_seed_worklist(
841850
effective_vis
842851
.is_public_at_level(Level::Reachable)
843852
.then_some(id)
853+
.filter(|&id|
854+
// checks impl-of-traits, impl-item-of-trait-items and pub structs with all public fields later
855+
match tcx.def_kind(id) {
856+
DefKind::Impl { of_trait: true } => false,
857+
DefKind::AssocFn => {
858+
let assoc_item = tcx.associated_item(id);
859+
!matches!(assoc_item.container, AssocItemContainer::ImplContainer)
860+
|| assoc_item.trait_item_def_id.is_none()
861+
}
862+
DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()),
863+
_ => true
864+
})
844865
.map(|id| (id, ComesFromAllowExpect::No))
845866
})
846867
// Seed entry point

library/core/src/clone.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ pub macro Clone($item:item) {
184184
//
185185
// These structs should never appear in user code.
186186
#[doc(hidden)]
187+
#[allow(dead_code)]
187188
#[allow(missing_debug_implementations)]
188189
#[unstable(
189190
feature = "derive_clone_copy",
@@ -194,6 +195,7 @@ pub struct AssertParamIsClone<T: Clone + ?Sized> {
194195
_field: crate::marker::PhantomData<T>,
195196
}
196197
#[doc(hidden)]
198+
#[allow(dead_code)]
197199
#[allow(missing_debug_implementations)]
198200
#[unstable(
199201
feature = "derive_clone_copy",

tests/ui/coherence/re-rebalance-coherence.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
extern crate re_rebalance_coherence_lib as lib;
55
use lib::*;
66

7+
#[allow(dead_code)]
78
struct Oracle;
89
impl Backend for Oracle {}
910
impl<'a, T:'a, Tab> QueryFragment<Oracle> for BatchInsert<'a, T, Tab> {}

tests/ui/const-generics/defaults/repr-c-issue-82792.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
//@ run-pass
44

5+
#[allow(dead_code)]
56
#[repr(C)]
67
pub struct Loaf<T: Sized, const N: usize = 1> {
78
head: [T; N],

tests/ui/const-generics/generic_const_exprs/associated-consts.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ impl BlockCipher for BarCipher {
1616
const BLOCK_SIZE: usize = 32;
1717
}
1818

19-
pub struct Block<C>(#[allow(dead_code)] C);
19+
#[allow(dead_code)]
20+
pub struct Block<C>( C);
2021

2122
pub fn test<C: BlockCipher, const M: usize>()
2223
where

tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use std::mem::MaybeUninit;
88

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

tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#![forbid(dead_code)]
22

33
#[derive(Debug)]
4-
pub struct Whatever {
4+
pub struct Whatever { //~ ERROR struct `Whatever` is never constructed
55
pub field0: (),
6-
field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read
6+
field1: (),
77
field2: (),
88
field3: (),
99
field4: (),

tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,9 @@
1-
error: fields `field1`, `field2`, `field3`, and `field4` are never read
2-
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5
1+
error: struct `Whatever` is never constructed
2+
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:4:12
33
|
44
LL | pub struct Whatever {
5-
| -------- fields in this struct
6-
LL | pub field0: (),
7-
LL | field1: (),
8-
| ^^^^^^
9-
LL | field2: (),
10-
| ^^^^^^
11-
LL | field3: (),
12-
| ^^^^^^
13-
LL | field4: (),
14-
| ^^^^^^
5+
| ^^^^^^^^
156
|
16-
= note: `Whatever` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
177
note: the lint level is defined here
188
--> $DIR/clone-debug-dead-code-in-the-same-struct.rs:1:11
199
|

tests/ui/lint/dead-code/lint-dead-code-1.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ struct SemiUsedStruct;
4646
impl SemiUsedStruct {
4747
fn la_la_la() {}
4848
}
49-
struct StructUsedAsField;
49+
struct StructUsedAsField; //~ ERROR struct `StructUsedAsField` is never constructed
5050
pub struct StructUsedInEnum;
5151
struct StructUsedInGeneric;
52-
pub struct PubStruct2 {
52+
pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed
5353
#[allow(dead_code)]
5454
struct_used_as_field: *const StructUsedAsField
5555
}

tests/ui/lint/dead-code/lint-dead-code-1.stderr

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ error: struct `PrivStruct` is never constructed
2222
LL | struct PrivStruct;
2323
| ^^^^^^^^^^
2424

25+
error: struct `StructUsedAsField` is never constructed
26+
--> $DIR/lint-dead-code-1.rs:49:8
27+
|
28+
LL | struct StructUsedAsField;
29+
| ^^^^^^^^^^^^^^^^^
30+
31+
error: struct `PubStruct2` is never constructed
32+
--> $DIR/lint-dead-code-1.rs:52:12
33+
|
34+
LL | pub struct PubStruct2 {
35+
| ^^^^^^^^^^
36+
2537
error: enum `priv_enum` is never used
2638
--> $DIR/lint-dead-code-1.rs:64:6
2739
|
@@ -67,5 +79,5 @@ error: struct `Bar` is never constructed
6779
LL | pub struct Bar;
6880
| ^^^
6981

70-
error: aborting due to 10 previous errors
82+
error: aborting due to 12 previous errors
7183

0 commit comments

Comments
 (0)