Skip to content

Commit c99b3f2

Browse files
authored
Rollup merge of rust-lang#122382 - mu001999:dead_code/enhance, r=petrochenkov
Detect unused structs which implement private traits Fixes rust-lang#122361
2 parents 9c8a58f + 9696835 commit c99b3f2

File tree

6 files changed

+84
-17
lines changed

6 files changed

+84
-17
lines changed

compiler/rustc_passes/src/dead.rs

+33-16
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
425425
&& let ItemKind::Impl(impl_ref) =
426426
self.tcx.hir().expect_item(local_impl_id).kind
427427
{
428-
if self.tcx.visibility(trait_id).is_public()
429-
&& matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
428+
if matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
430429
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
431430
{
431+
// skip methods of private ty,
432+
// they would be solved in `solve_rest_impl_items`
432433
continue;
433434
}
434435

@@ -485,32 +486,46 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
485486

486487
fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) {
487488
let mut ready;
488-
(ready, unsolved_impl_items) = unsolved_impl_items
489-
.into_iter()
490-
.partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id));
489+
(ready, unsolved_impl_items) =
490+
unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
491+
self.impl_item_with_used_self(impl_id, impl_item_id)
492+
});
491493

492494
while !ready.is_empty() {
493495
self.worklist =
494496
ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect();
495497
self.mark_live_symbols();
496498

497-
(ready, unsolved_impl_items) = unsolved_impl_items
498-
.into_iter()
499-
.partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id));
499+
(ready, unsolved_impl_items) =
500+
unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
501+
self.impl_item_with_used_self(impl_id, impl_item_id)
502+
});
500503
}
501504
}
502505

503-
fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId) -> bool {
506+
fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool {
504507
if let TyKind::Path(hir::QPath::Resolved(_, path)) =
505508
self.tcx.hir().item(impl_id).expect_impl().self_ty.kind
506509
&& let Res::Def(def_kind, def_id) = path.res
507510
&& let Some(local_def_id) = def_id.as_local()
508511
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
509512
{
510-
self.live_symbols.contains(&local_def_id)
511-
} else {
512-
false
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+
if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
520+
&& let Some(local_id) = trait_item_id.as_local()
521+
{
522+
// for the private method, we can know the trait item is used or not,
523+
// 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);
526+
}
513527
}
528+
false
514529
}
515530
}
516531

@@ -745,20 +760,22 @@ fn check_item<'tcx>(
745760
matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None);
746761
}
747762

748-
// for impl trait blocks, mark associate functions live if the trait is public
763+
// for trait impl blocks,
764+
// mark the method live if the self_ty is public,
765+
// or the method is public and may construct self
749766
if of_trait
750767
&& (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
751768
|| tcx.visibility(local_def_id).is_public()
752769
&& (ty_is_pub || may_construct_self))
753770
{
754771
worklist.push((local_def_id, ComesFromAllowExpect::No));
755-
} else if of_trait && tcx.visibility(local_def_id).is_public() {
756-
// pub method && private ty & methods not construct self
757-
unsolved_impl_items.push((id, local_def_id));
758772
} else if let Some(comes_from_allow) =
759773
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
760774
{
761775
worklist.push((local_def_id, comes_from_allow));
776+
} else if of_trait {
777+
// private method || public method not constructs self
778+
unsolved_impl_items.push((id, local_def_id));
762779
}
763780
}
764781
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#![deny(dead_code)]
2+
3+
struct Used;
4+
struct Unused; //~ ERROR struct `Unused` is never constructed
5+
6+
pub trait PubTrait {
7+
fn foo(&self) -> Self;
8+
}
9+
10+
impl PubTrait for Used {
11+
fn foo(&self) -> Self { Used }
12+
}
13+
14+
impl PubTrait for Unused {
15+
fn foo(&self) -> Self { Unused }
16+
}
17+
18+
trait PriTrait {
19+
fn foo(&self) -> Self;
20+
}
21+
22+
impl PriTrait for Used {
23+
fn foo(&self) -> Self { Used }
24+
}
25+
26+
impl PriTrait for Unused {
27+
fn foo(&self) -> Self { Unused }
28+
}
29+
30+
fn main() {
31+
let t = Used;
32+
let _t = <Used as PubTrait>::foo(&t);
33+
let _t = <Used as PriTrait>::foo(&t);
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: struct `Unused` is never constructed
2+
--> $DIR/unused-adt-impls-trait.rs:4:8
3+
|
4+
LL | struct Unused;
5+
| ^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unused-adt-impls-trait.rs:1:9
9+
|
10+
LL | #![deny(dead_code)]
11+
| ^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+

tests/ui/rust-2021/inherent-dyn-collision.fixed

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod inner {
2525
// having a struct of the same name as the trait in-scope, while *also*
2626
// implementing the trait for that struct but **without** importing the
2727
// trait itself into scope
28+
#[allow(dead_code)]
2829
struct TryIntoU32;
2930

3031
impl super::TryIntoU32 for TryIntoU32 {

tests/ui/rust-2021/inherent-dyn-collision.rs

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod inner {
2525
// having a struct of the same name as the trait in-scope, while *also*
2626
// implementing the trait for that struct but **without** importing the
2727
// trait itself into scope
28+
#[allow(dead_code)]
2829
struct TryIntoU32;
2930

3031
impl super::TryIntoU32 for TryIntoU32 {

tests/ui/rust-2021/inherent-dyn-collision.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: trait method `try_into` will become ambiguous in Rust 2021
2-
--> $DIR/inherent-dyn-collision.rs:41:9
2+
--> $DIR/inherent-dyn-collision.rs:42:9
33
|
44
LL | get_dyn_trait().try_into().unwrap()
55
| ^^^^^^^^^^^^^^^ help: disambiguate the method call: `(&*get_dyn_trait())`

0 commit comments

Comments
 (0)