@@ -15,7 +15,7 @@ use rustc_hir::{Node, PatKind, TyKind};
15
15
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
16
16
use rustc_middle::middle::privacy::Level;
17
17
use rustc_middle::query::Providers;
18
- use rustc_middle::ty::{self, TyCtxt};
18
+ use rustc_middle::ty::{self, AssocItemContainer, TyCtxt};
19
19
use rustc_middle::{bug, span_bug};
20
20
use rustc_session::lint;
21
21
use rustc_session::lint::builtin::DEAD_CODE;
@@ -44,16 +44,63 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
44
44
)
45
45
}
46
46
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 {
48
83
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
49
84
&& let Res::Def(def_kind, def_id) = path.res
50
85
&& def_id.is_local()
51
- && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
52
86
{
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
+ };
56
101
}
102
+
103
+ Publicness::new(true, true)
57
104
}
58
105
59
106
/// Determine if a work from the worklist is coming from the a `#[allow]`
@@ -427,9 +474,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
427
474
{
428
475
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
429
476
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
477
+ .ty_and_all_fields_are_public
430
478
{
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`
433
482
continue;
434
483
}
435
484
@@ -510,22 +559,21 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
510
559
&& let Some(local_def_id) = def_id.as_local()
511
560
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
512
561
{
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
-
519
562
if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
520
563
&& let Some(local_id) = trait_item_id.as_local()
521
564
{
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,
523
566
// 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)
526
573
}
574
+ } else {
575
+ false
527
576
}
528
- false
529
577
}
530
578
}
531
579
@@ -747,7 +795,9 @@ fn check_item<'tcx>(
747
795
.iter()
748
796
.filter_map(|def_id| def_id.as_local());
749
797
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);
751
801
752
802
// And we access the Map here to get HirId from LocalDefId
753
803
for local_def_id in local_def_ids {
@@ -763,18 +813,20 @@ fn check_item<'tcx>(
763
813
// for trait impl blocks,
764
814
// mark the method live if the self_ty is public,
765
815
// 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)
770
819
{
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
771
823
worklist.push((local_def_id, ComesFromAllowExpect::No));
772
824
} else if let Some(comes_from_allow) =
773
825
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
774
826
{
775
827
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
778
830
unsolved_impl_items.push((id, local_def_id));
779
831
}
780
832
}
@@ -841,6 +893,14 @@ fn create_and_seed_worklist(
841
893
effective_vis
842
894
.is_public_at_level(Level::Reachable)
843
895
.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
+ })
844
904
.map(|id| (id, ComesFromAllowExpect::No))
845
905
})
846
906
// Seed entry point
@@ -1113,10 +1173,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
1113
1173
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
1114
1174
{
1115
1175
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
1117
1177
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
1120
1185
{
1121
1186
continue;
1122
1187
}
0 commit comments