Skip to content

Commit 13314df

Browse files
authored
Rollup merge of rust-lang#125572 - mu001999-contrib:dead/enhance, r=pnkfelix
Detect pub structs never constructed and unused associated constants <!-- If this PR is related to an unstable feature or an otherwise tracked effort, please link to the relevant tracking issue here. If you don't know of a related tracking issue or there are none, feel free to ignore this. This PR will get automatically assigned to a reviewer. In case you would like a specific user to review your work, you can assign it to them by using r​? <reviewer name> --> Lints never constructed public structs. If we don't provide public methods to construct public structs with private fields, and don't construct them in the local crate. They would be never constructed. So that we can detect such public structs. --- Update: Also lints unused associated constants in traits.
2 parents 6e534c7 + 35130d7 commit 13314df

32 files changed

+272
-88
lines changed

compiler/rustc_passes/src/dead.rs

+93-28
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,63 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
4444
)
4545
}
4646

47-
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
47+
struct Publicness {
48+
ty_is_public: bool,
49+
ty_and_all_fields_are_public: bool,
50+
}
51+
52+
impl Publicness {
53+
fn new(ty_is_public: bool, ty_and_all_fields_are_public: bool) -> Self {
54+
Self { ty_is_public, ty_and_all_fields_are_public }
55+
}
56+
}
57+
58+
fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
59+
// treat PhantomData and positional ZST as public,
60+
// we don't want to lint types which only have them,
61+
// cause it's a common way to use such types to check things like well-formedness
62+
tcx.adt_def(id).all_fields().all(|field| {
63+
let field_type = tcx.type_of(field.did).instantiate_identity();
64+
if field_type.is_phantom_data() {
65+
return true;
66+
}
67+
let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit());
68+
if is_positional
69+
&& tcx
70+
.layout_of(tcx.param_env(field.did).and(field_type))
71+
.map_or(true, |layout| layout.is_zst())
72+
{
73+
return true;
74+
}
75+
field.vis.is_public()
76+
})
77+
}
78+
79+
/// check struct and its fields are public or not,
80+
/// for enum and union, just check they are public,
81+
/// and doesn't solve types like &T for now, just skip them
82+
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness {
4883
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
4984
&& let Res::Def(def_kind, def_id) = path.res
5085
&& def_id.is_local()
51-
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
5286
{
53-
tcx.visibility(def_id).is_public()
54-
} else {
55-
true
87+
return match def_kind {
88+
DefKind::Enum | DefKind::Union => {
89+
let ty_is_public = tcx.visibility(def_id).is_public();
90+
Publicness::new(ty_is_public, ty_is_public)
91+
}
92+
DefKind::Struct => {
93+
let ty_is_public = tcx.visibility(def_id).is_public();
94+
Publicness::new(
95+
ty_is_public,
96+
ty_is_public && struct_all_fields_are_public(tcx, def_id),
97+
)
98+
}
99+
_ => Publicness::new(true, true),
100+
};
56101
}
102+
103+
Publicness::new(true, true)
57104
}
58105

59106
/// Determine if a work from the worklist is coming from the a `#[allow]`
@@ -427,9 +474,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
427474
{
428475
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
429476
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
477+
.ty_and_all_fields_are_public
430478
{
431-
// skip methods of private ty,
432-
// they would be solved in `solve_rest_impl_items`
479+
// skip impl-items of non pure pub ty,
480+
// cause we don't know the ty is constructed or not,
481+
// check these later in `solve_rest_impl_items`
433482
continue;
434483
}
435484

@@ -510,22 +559,21 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
510559
&& let Some(local_def_id) = def_id.as_local()
511560
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
512561
{
513-
if self.tcx.visibility(impl_item_id).is_public() {
514-
// for the public method, we don't know the trait item is used or not,
515-
// so we mark the method live if the self is used
516-
return self.live_symbols.contains(&local_def_id);
517-
}
518-
519562
if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
520563
&& let Some(local_id) = trait_item_id.as_local()
521564
{
522-
// for the private method, we can know the trait item is used or not,
565+
// for the local impl item, we can know the trait item is used or not,
523566
// so we mark the method live if the self is used and the trait item is used
524-
return self.live_symbols.contains(&local_id)
525-
&& self.live_symbols.contains(&local_def_id);
567+
self.live_symbols.contains(&local_id) && self.live_symbols.contains(&local_def_id)
568+
} else {
569+
// for the foreign method and inherent pub method,
570+
// we don't know the trait item or the method is used or not,
571+
// so we mark the method live if the self is used
572+
self.live_symbols.contains(&local_def_id)
526573
}
574+
} else {
575+
false
527576
}
528-
false
529577
}
530578
}
531579

@@ -747,7 +795,9 @@ fn check_item<'tcx>(
747795
.iter()
748796
.filter_map(|def_id| def_id.as_local());
749797

750-
let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty);
798+
let self_ty = tcx.hir().item(id).expect_impl().self_ty;
799+
let Publicness { ty_is_public, ty_and_all_fields_are_public } =
800+
ty_ref_to_pub_struct(tcx, self_ty);
751801

752802
// And we access the Map here to get HirId from LocalDefId
753803
for local_def_id in local_def_ids {
@@ -763,18 +813,20 @@ fn check_item<'tcx>(
763813
// for trait impl blocks,
764814
// mark the method live if the self_ty is public,
765815
// or the method is public and may construct self
766-
if of_trait
767-
&& (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
768-
|| tcx.visibility(local_def_id).is_public()
769-
&& (ty_is_pub || may_construct_self))
816+
if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy)
817+
|| tcx.visibility(local_def_id).is_public()
818+
&& (ty_and_all_fields_are_public || may_construct_self)
770819
{
820+
// if the impl item is public,
821+
// and the ty may be constructed or can be constructed in foreign crates,
822+
// mark the impl item live
771823
worklist.push((local_def_id, ComesFromAllowExpect::No));
772824
} else if let Some(comes_from_allow) =
773825
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
774826
{
775827
worklist.push((local_def_id, comes_from_allow));
776-
} else if of_trait {
777-
// private method || public method not constructs self
828+
} else if of_trait || tcx.visibility(local_def_id).is_public() && ty_is_public {
829+
// private impl items of traits || public impl items not constructs self
778830
unsolved_impl_items.push((id, local_def_id));
779831
}
780832
}
@@ -841,6 +893,14 @@ fn create_and_seed_worklist(
841893
effective_vis
842894
.is_public_at_level(Level::Reachable)
843895
.then_some(id)
896+
.filter(|&id|
897+
// checks impls, impl-items and pub structs with all public fields later
898+
match tcx.def_kind(id) {
899+
DefKind::Impl { .. } => false,
900+
DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
901+
DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()),
902+
_ => true
903+
})
844904
.map(|id| (id, ComesFromAllowExpect::No))
845905
})
846906
// Seed entry point
@@ -1113,10 +1173,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
11131173
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
11141174
{
11151175
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
1116-
// We have diagnosed unused methods in traits
1176+
// We have diagnosed unused assoc consts and fns in traits
11171177
if matches!(def_kind, DefKind::Impl { of_trait: true })
1118-
&& tcx.def_kind(def_id) == DefKind::AssocFn
1119-
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
1178+
&& matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn)
1179+
// skip unused public inherent methods,
1180+
// cause we have diagnosed unconstructed struct
1181+
|| matches!(def_kind, DefKind::Impl { of_trait: false })
1182+
&& tcx.visibility(def_id).is_public()
1183+
&& ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public
1184+
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy
11201185
{
11211186
continue;
11221187
}

tests/codegen-units/item-collection/generic-impl.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ impl<T> Struct<T> {
2222
}
2323
}
2424

25-
pub struct LifeTimeOnly<'a> {
25+
pub struct _LifeTimeOnly<'a> {
2626
_a: &'a u32,
2727
}
2828

29-
impl<'a> LifeTimeOnly<'a> {
30-
//~ MONO_ITEM fn LifeTimeOnly::<'_>::foo
29+
impl<'a> _LifeTimeOnly<'a> {
30+
//~ MONO_ITEM fn _LifeTimeOnly::<'_>::foo
3131
pub fn foo(&self) {}
32-
//~ MONO_ITEM fn LifeTimeOnly::<'_>::bar
32+
//~ MONO_ITEM fn _LifeTimeOnly::<'_>::bar
3333
pub fn bar(&'a self) {}
34-
//~ MONO_ITEM fn LifeTimeOnly::<'_>::baz
34+
//~ MONO_ITEM fn _LifeTimeOnly::<'_>::baz
3535
pub fn baz<'b>(&'b self) {}
3636

3737
pub fn non_instantiated<T>(&self) {}

tests/codegen-units/item-collection/overloaded-operators.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,44 @@
55

66
use std::ops::{Add, Deref, Index, IndexMut};
77

8-
pub struct Indexable {
8+
pub struct _Indexable {
99
data: [u8; 3],
1010
}
1111

12-
impl Index<usize> for Indexable {
12+
impl Index<usize> for _Indexable {
1313
type Output = u8;
1414

15-
//~ MONO_ITEM fn <Indexable as std::ops::Index<usize>>::index
15+
//~ MONO_ITEM fn <_Indexable as std::ops::Index<usize>>::index
1616
fn index(&self, index: usize) -> &Self::Output {
1717
if index >= 3 { &self.data[0] } else { &self.data[index] }
1818
}
1919
}
2020

21-
impl IndexMut<usize> for Indexable {
22-
//~ MONO_ITEM fn <Indexable as std::ops::IndexMut<usize>>::index_mut
21+
impl IndexMut<usize> for _Indexable {
22+
//~ MONO_ITEM fn <_Indexable as std::ops::IndexMut<usize>>::index_mut
2323
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
2424
if index >= 3 { &mut self.data[0] } else { &mut self.data[index] }
2525
}
2626
}
2727

28-
//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::eq
29-
//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::ne
28+
//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::eq
29+
//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::ne
3030
#[derive(PartialEq)]
31-
pub struct Equatable(u32);
31+
pub struct _Equatable(u32);
3232

33-
impl Add<u32> for Equatable {
33+
impl Add<u32> for _Equatable {
3434
type Output = u32;
3535

36-
//~ MONO_ITEM fn <Equatable as std::ops::Add<u32>>::add
36+
//~ MONO_ITEM fn <_Equatable as std::ops::Add<u32>>::add
3737
fn add(self, rhs: u32) -> u32 {
3838
self.0 + rhs
3939
}
4040
}
4141

42-
impl Deref for Equatable {
42+
impl Deref for _Equatable {
4343
type Target = u32;
4444

45-
//~ MONO_ITEM fn <Equatable as std::ops::Deref>::deref
45+
//~ MONO_ITEM fn <_Equatable as std::ops::Deref>::deref
4646
fn deref(&self) -> &Self::Target {
4747
&self.0
4848
}

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/issues/issue-5708.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub trait MyTrait<T> {
4444
fn dummy(&self, t: T) -> T { panic!() }
4545
}
4646

47+
#[allow(dead_code)]
4748
pub struct MyContainer<'a, T:'a> {
4849
foos: Vec<&'a (dyn MyTrait<T>+'a)> ,
4950
}

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -46,11 +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 {
53-
#[allow(dead_code)]
52+
pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed
5453
struct_used_as_field: *const StructUsedAsField
5554
}
5655

0 commit comments

Comments
 (0)