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

+26-5
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

+2
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

+1
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

+1
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

+2-1
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

+1
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

+2-2
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

+3-13
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

+2-2
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

+13-1
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

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![deny(dead_code)]
2+
3+
pub struct T(()); //~ ERROR struct `T` is never constructed
4+
5+
impl Clone for T {
6+
fn clone(&self) -> T {
7+
T(())
8+
}
9+
}
10+
11+
pub trait Trait {
12+
fn foo(&self) {}
13+
}
14+
15+
impl Trait for T {}
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: struct `T` is never constructed
2+
--> $DIR/unused_pub_struct.rs:3:12
3+
|
4+
LL | pub struct T(());
5+
| ^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unused_pub_struct.rs:1:9
9+
|
10+
LL | #![deny(dead_code)]
11+
| ^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+

tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// Regression test for issues #100790 and #106439.
22
//@ run-rustfix
33

4-
pub struct Example(#[allow(dead_code)] usize)
4+
#[allow(dead_code)]
5+
pub struct Example(usize)
56
where
67
(): Sized;
78
//~^^^ ERROR where clauses are not allowed before tuple struct bodies

tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
// Regression test for issues #100790 and #106439.
22
//@ run-rustfix
33

4+
#[allow(dead_code)]
45
pub struct Example
56
where
67
(): Sized,
7-
(#[allow(dead_code)] usize);
8+
(usize);
89
//~^^^ ERROR where clauses are not allowed before tuple struct bodies
910

1011
struct _Demo

tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
error: where clauses are not allowed before tuple struct bodies
2-
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1
2+
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1
33
|
44
LL | pub struct Example
55
| ------- while parsing this tuple struct
66
LL | / where
77
LL | | (): Sized,
88
| |______________^ unexpected where clause
9-
LL | (#[allow(dead_code)] usize);
10-
| --------------------------- the struct body
9+
LL | (usize);
10+
| ------- the struct body
1111
|
1212
help: move the body before the where clause
1313
|
14-
LL ~ pub struct Example(#[allow(dead_code)] usize)
14+
LL ~ pub struct Example(usize)
1515
LL | where
1616
LL ~ (): Sized;
1717
|
1818

1919
error: where clauses are not allowed before tuple struct bodies
20-
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1
20+
--> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1
2121
|
2222
LL | struct _Demo
2323
| ----- while parsing this tuple struct

tests/ui/pub/pub-ident-struct-4.fixed

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//@ run-rustfix
22

3-
pub struct T(#[allow(dead_code)] String);
3+
#[allow(dead_code)]
4+
pub struct T(String);
45
//~^ ERROR missing `struct` for struct definition
56

67
fn main() {}

tests/ui/pub/pub-ident-struct-4.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//@ run-rustfix
22

3-
pub T(#[allow(dead_code)] String);
3+
#[allow(dead_code)]
4+
pub T(String);
45
//~^ ERROR missing `struct` for struct definition
56

67
fn main() {}

tests/ui/pub/pub-ident-struct-4.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
error: missing `struct` for struct definition
2-
--> $DIR/pub-ident-struct-4.rs:3:4
2+
--> $DIR/pub-ident-struct-4.rs:4:4
33
|
4-
LL | pub T(#[allow(dead_code)] String);
4+
LL | pub T(String);
55
| ^
66
|
77
help: add `struct` here to parse `T` as a public struct
88
|
9-
LL | pub struct T(#[allow(dead_code)] String);
9+
LL | pub struct T(String);
1010
| ++++++
1111

1212
error: aborting due to 1 previous error

tests/ui/regions/regions-issue-21422.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
//@ pretty-expanded FIXME #23616
77

8+
#[allow(dead_code)]
89
pub struct P<'a> {
910
_ptr: *const &'a u8,
1011
}

tests/ui/structs-enums/newtype-struct-with-dtor.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
#![allow(unused_variables)]
44
//@ pretty-expanded FIXME #23616
55

6+
#[allow(dead_code)]
67
pub struct Fd(u32);
78

9+
#[allow(dead_code)]
810
fn foo(a: u32) {}
911

1012
impl Drop for Fd {
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@ run-pass
2-
pub struct Z(#[allow(dead_code)] &'static Z);
2+
#[allow(dead_code)]
3+
pub struct Z(&'static Z);
34

45
pub fn main() {}

tests/ui/suggestions/derive-clone-for-eq.fixed

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//@ run-rustfix
22
// https://github.com/rust-lang/rust/issues/79076
33

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

tests/ui/suggestions/derive-clone-for-eq.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//@ run-rustfix
22
// https://github.com/rust-lang/rust/issues/79076
33

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

tests/ui/suggestions/derive-clone-for-eq.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error[E0277]: the trait bound `T: Clone` is not satisfied
2-
--> $DIR/derive-clone-for-eq.rs:4:17
2+
--> $DIR/derive-clone-for-eq.rs:5:17
33
|
44
LL | #[derive(Clone, Eq)]
55
| ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct<T>: PartialEq`
66
|
77
note: required for `Struct<T>` to implement `PartialEq`
8-
--> $DIR/derive-clone-for-eq.rs:7:19
8+
--> $DIR/derive-clone-for-eq.rs:8:19
99
|
1010
LL | impl<T: Clone, U> PartialEq<U> for Struct<T>
1111
| ----- ^^^^^^^^^^^^ ^^^^^^^^^

0 commit comments

Comments
 (0)