Skip to content

Commit c69fda7

Browse files
committed
Auto merge of rust-lang#121752 - mu001999:dead_code/improve, r=pnkfelix
Detect unused struct impls pub trait Fixes rust-lang#47851
2 parents cd81f5b + c73a7f0 commit c69fda7

File tree

10 files changed

+121
-82
lines changed

10 files changed

+121
-82
lines changed

compiler/rustc_passes/src/dead.rs

+85-11
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, Visibility};
18+
use rustc_middle::ty::{self, TyCtxt};
1919
use rustc_session::lint;
2020
use rustc_session::lint::builtin::DEAD_CODE;
2121
use rustc_span::symbol::{sym, Symbol};
@@ -45,6 +45,18 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
4545
)
4646
}
4747

48+
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
49+
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
50+
&& let Res::Def(def_kind, def_id) = path.res
51+
&& def_id.is_local()
52+
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
53+
{
54+
tcx.visibility(def_id).is_public()
55+
} else {
56+
true
57+
}
58+
}
59+
4860
/// Determine if a work from the worklist is coming from the a `#[allow]`
4961
/// or a `#[expect]` of `dead_code`
5062
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
@@ -415,6 +427,13 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
415427
&& let ItemKind::Impl(impl_ref) =
416428
self.tcx.hir().expect_item(local_impl_id).kind
417429
{
430+
if self.tcx.visibility(trait_id).is_public()
431+
&& matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
432+
&& !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
433+
{
434+
continue;
435+
}
436+
418437
// mark self_ty live
419438
intravisit::walk_ty(self, impl_ref.self_ty);
420439
if let Some(&impl_item_id) =
@@ -465,6 +484,36 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
465484
}
466485
}
467486
}
487+
488+
fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) {
489+
let mut ready;
490+
(ready, unsolved_impl_items) = unsolved_impl_items
491+
.into_iter()
492+
.partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id));
493+
494+
while !ready.is_empty() {
495+
self.worklist =
496+
ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect();
497+
self.mark_live_symbols();
498+
499+
(ready, unsolved_impl_items) = unsolved_impl_items
500+
.into_iter()
501+
.partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id));
502+
}
503+
}
504+
505+
fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId) -> bool {
506+
if let TyKind::Path(hir::QPath::Resolved(_, path)) =
507+
self.tcx.hir().item(impl_id).expect_impl().self_ty.kind
508+
&& let Res::Def(def_kind, def_id) = path.res
509+
&& let Some(local_def_id) = def_id.as_local()
510+
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
511+
{
512+
self.live_symbols.contains(&local_def_id)
513+
} else {
514+
false
515+
}
516+
}
468517
}
469518

470519
impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
@@ -652,6 +701,7 @@ fn check_item<'tcx>(
652701
tcx: TyCtxt<'tcx>,
653702
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
654703
struct_constructors: &mut LocalDefIdMap<LocalDefId>,
704+
unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>,
655705
id: hir::ItemId,
656706
) {
657707
let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
@@ -683,16 +733,33 @@ fn check_item<'tcx>(
683733
.iter()
684734
.filter_map(|def_id| def_id.as_local());
685735

736+
let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty);
737+
686738
// And we access the Map here to get HirId from LocalDefId
687-
for id in local_def_ids {
739+
for local_def_id in local_def_ids {
740+
// check the function may construct Self
741+
let mut may_construct_self = true;
742+
if let Some(hir_id) = tcx.opt_local_def_id_to_hir_id(local_def_id)
743+
&& let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id)
744+
{
745+
may_construct_self =
746+
matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None);
747+
}
748+
688749
// for impl trait blocks, mark associate functions live if the trait is public
689750
if of_trait
690-
&& (!matches!(tcx.def_kind(id), DefKind::AssocFn)
691-
|| tcx.local_visibility(id) == Visibility::Public)
751+
&& (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
752+
|| tcx.visibility(local_def_id).is_public()
753+
&& (ty_is_pub || may_construct_self))
692754
{
693-
worklist.push((id, ComesFromAllowExpect::No));
694-
} else if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, id) {
695-
worklist.push((id, comes_from_allow));
755+
worklist.push((local_def_id, ComesFromAllowExpect::No));
756+
} else if of_trait && tcx.visibility(local_def_id).is_public() {
757+
// pub method && private ty & methods not construct self
758+
unsolved_impl_items.push((id, local_def_id));
759+
} else if let Some(comes_from_allow) =
760+
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
761+
{
762+
worklist.push((local_def_id, comes_from_allow));
696763
}
697764
}
698765
}
@@ -743,9 +810,14 @@ fn check_foreign_item(
743810

744811
fn create_and_seed_worklist(
745812
tcx: TyCtxt<'_>,
746-
) -> (Vec<(LocalDefId, ComesFromAllowExpect)>, LocalDefIdMap<LocalDefId>) {
813+
) -> (
814+
Vec<(LocalDefId, ComesFromAllowExpect)>,
815+
LocalDefIdMap<LocalDefId>,
816+
Vec<(hir::ItemId, LocalDefId)>,
817+
) {
747818
let effective_visibilities = &tcx.effective_visibilities(());
748819
// see `MarkSymbolVisitor::struct_constructors`
820+
let mut unsolved_impl_item = Vec::new();
749821
let mut struct_constructors = Default::default();
750822
let mut worklist = effective_visibilities
751823
.iter()
@@ -764,7 +836,7 @@ fn create_and_seed_worklist(
764836

765837
let crate_items = tcx.hir_crate_items(());
766838
for id in crate_items.items() {
767-
check_item(tcx, &mut worklist, &mut struct_constructors, id);
839+
check_item(tcx, &mut worklist, &mut struct_constructors, &mut unsolved_impl_item, id);
768840
}
769841

770842
for id in crate_items.trait_items() {
@@ -775,14 +847,14 @@ fn create_and_seed_worklist(
775847
check_foreign_item(tcx, &mut worklist, id);
776848
}
777849

778-
(worklist, struct_constructors)
850+
(worklist, struct_constructors, unsolved_impl_item)
779851
}
780852

781853
fn live_symbols_and_ignored_derived_traits(
782854
tcx: TyCtxt<'_>,
783855
(): (),
784856
) -> (LocalDefIdSet, LocalDefIdMap<Vec<(DefId, DefId)>>) {
785-
let (worklist, struct_constructors) = create_and_seed_worklist(tcx);
857+
let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx);
786858
let mut symbol_visitor = MarkSymbolVisitor {
787859
worklist,
788860
tcx,
@@ -796,6 +868,8 @@ fn live_symbols_and_ignored_derived_traits(
796868
ignored_derived_traits: Default::default(),
797869
};
798870
symbol_visitor.mark_live_symbols();
871+
symbol_visitor.solve_rest_impl_items(unsolved_impl_items);
872+
799873
(symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits)
800874
}
801875

src/tools/clippy/clippy_lints/src/loops/utils.rs

+2-58
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use clippy_utils::ty::{has_iter_method, implements_trait};
22
use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
33
use rustc_ast::ast::{LitIntType, LitKind};
44
use rustc_errors::Applicability;
5-
use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor};
6-
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt};
5+
use rustc_hir::intravisit::{walk_expr, walk_local, Visitor};
6+
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, PatKind};
77
use rustc_lint::LateContext;
88
use rustc_middle::hir::nested_filter;
99
use rustc_middle::ty::{self, Ty};
@@ -253,62 +253,6 @@ fn is_conditional(expr: &Expr<'_>) -> bool {
253253
matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..))
254254
}
255255

256-
#[derive(PartialEq, Eq)]
257-
pub(super) enum Nesting {
258-
Unknown, // no nesting detected yet
259-
RuledOut, // the iterator is initialized or assigned within scope
260-
LookFurther, // no nesting detected, no further walk required
261-
}
262-
263-
use self::Nesting::{LookFurther, RuledOut, Unknown};
264-
265-
pub(super) struct LoopNestVisitor {
266-
pub(super) hir_id: HirId,
267-
pub(super) iterator: HirId,
268-
pub(super) nesting: Nesting,
269-
}
270-
271-
impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
272-
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
273-
if stmt.hir_id == self.hir_id {
274-
self.nesting = LookFurther;
275-
} else if self.nesting == Unknown {
276-
walk_stmt(self, stmt);
277-
}
278-
}
279-
280-
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
281-
if self.nesting != Unknown {
282-
return;
283-
}
284-
if expr.hir_id == self.hir_id {
285-
self.nesting = LookFurther;
286-
return;
287-
}
288-
match expr.kind {
289-
ExprKind::Assign(path, _, _) | ExprKind::AssignOp(_, path, _) => {
290-
if path_to_local_id(path, self.iterator) {
291-
self.nesting = RuledOut;
292-
}
293-
},
294-
_ => walk_expr(self, expr),
295-
}
296-
}
297-
298-
fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
299-
if self.nesting != Unknown {
300-
return;
301-
}
302-
if let PatKind::Binding(_, id, ..) = pat.kind {
303-
if id == self.iterator {
304-
self.nesting = RuledOut;
305-
return;
306-
}
307-
}
308-
walk_pat(self, pat);
309-
}
310-
}
311-
312256
/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
313257
/// actual `Iterator` that the loop uses.
314258
pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {

src/tools/clippy/clippy_lints/src/macro_use.rs

-6
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,6 @@ declare_clippy_lint! {
3030
"#[macro_use] is no longer needed"
3131
}
3232

33-
#[derive(Clone, Debug, PartialEq, Eq)]
34-
struct PathAndSpan {
35-
path: String,
36-
span: Span,
37-
}
38-
3933
/// `MacroRefData` includes the name of the macro.
4034
#[derive(Debug, Clone)]
4135
pub struct MacroRefData {

tests/ui/lint/dead-code/issue-41883.stderr

-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ error: struct `UnusedStruct` is never constructed
2929
|
3030
LL | struct UnusedStruct;
3131
| ^^^^^^^^^^^^
32-
|
33-
= note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
3432

3533
error: aborting due to 4 previous errors
3634

tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr

-2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ warning: struct `Foo` is never constructed
5656
|
5757
LL | struct Foo(usize, #[allow(unused)] usize);
5858
| ^^^
59-
|
60-
= note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
6159

6260
error: aborting due to 2 previous errors; 2 warnings emitted
6361

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

tests/ui/rust-2018/uniform-paths/issue-55779.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@ run-pass
1+
//@ check-pass
22
//@ edition:2018
33
//@ aux-crate:issue_55779_extern_trait=issue-55779-extern-trait.rs
44

tests/ui/traits/augmented-assignments-trait.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@ run-pass
1+
//@ check-pass
22
use std::ops::AddAssign;
33

44
struct Int(#[allow(dead_code)] i32);

tests/ui/traits/issue-33187.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//@ run-pass
1+
//@ check-pass
22

33
struct Foo<A: Repr>(<A as Repr>::Data);
44

0 commit comments

Comments
 (0)