Rollup merge of #142485 - mu001999-contrib:dead-code/adt-pattern, r=petrochenkov
Marks ADT live if it appears in pattern Marks ADT live if it appears in pattern, it implies the construction of the ADT. 1. Then we can detect unused private ADTs impl `Default`, without special logics for `Default` and other std traits. 2. We can also remove `rustc_trivial_field_reads` on `Default`, and the logic in `should_ignore_item` (introduced by rust-lang/rust#126302). Fixes rust-lang/rust#120770 Extracted from rust-lang/rust#128637. r? `@petrochenkov`
This commit is contained in:
commit
c93fac7d64
24 changed files with 161 additions and 67 deletions
|
|
@ -234,7 +234,14 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
pats: &[hir::PatField<'_>],
|
||||
) {
|
||||
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
|
||||
ty::Adt(adt, _) => adt.variant_of_res(res),
|
||||
ty::Adt(adt, _) => {
|
||||
// Marks the ADT live if its variant appears as the pattern,
|
||||
// considering cases when we have `let T(x) = foo()` and `fn foo<T>() -> T;`,
|
||||
// we will lose the liveness info of `T` cause we cannot mark it live when visiting `foo`.
|
||||
// Related issue: https://github.com/rust-lang/rust/issues/120770
|
||||
self.check_def_id(adt.did());
|
||||
adt.variant_of_res(res)
|
||||
}
|
||||
_ => span_bug!(lhs.span, "non-ADT in struct pattern"),
|
||||
};
|
||||
for pat in pats {
|
||||
|
|
@ -254,7 +261,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
dotdot: hir::DotDotPos,
|
||||
) {
|
||||
let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
|
||||
ty::Adt(adt, _) => adt.variant_of_res(res),
|
||||
ty::Adt(adt, _) => {
|
||||
// Marks the ADT live if its variant appears as the pattern
|
||||
self.check_def_id(adt.did());
|
||||
adt.variant_of_res(res)
|
||||
}
|
||||
_ => {
|
||||
self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
|
||||
return;
|
||||
|
|
@ -359,31 +370,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
return false;
|
||||
}
|
||||
|
||||
// don't ignore impls for Enums and pub Structs whose methods don't have self receiver,
|
||||
// cause external crate may call such methods to construct values of these types
|
||||
if let Some(local_impl_of) = impl_of.as_local()
|
||||
&& let Some(local_def_id) = def_id.as_local()
|
||||
&& let Some(fn_sig) =
|
||||
self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
|
||||
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
|
||||
&& let TyKind::Path(QPath::Resolved(_, path)) =
|
||||
self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind
|
||||
&& let Res::Def(def_kind, did) = path.res
|
||||
{
|
||||
match def_kind {
|
||||
// for example, #[derive(Default)] pub struct T(i32);
|
||||
// external crate can call T::default() to construct T,
|
||||
// so that don't ignore impl Default for pub Enum and Structs
|
||||
DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => {
|
||||
return false;
|
||||
}
|
||||
// don't ignore impl Default for Enums,
|
||||
// cause we don't know which variant is constructed
|
||||
DefKind::Enum => return false,
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
|
||||
&& self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
|
||||
{
|
||||
|
|
@ -494,38 +480,25 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
|
|||
impl_id: hir::ItemId,
|
||||
local_def_id: LocalDefId,
|
||||
) -> bool {
|
||||
if self.should_ignore_item(local_def_id.to_def_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let trait_def_id = match self.tcx.def_kind(local_def_id) {
|
||||
// assoc impl items of traits are live if the corresponding trait items are live
|
||||
DefKind::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id,
|
||||
DefKind::AssocFn => self
|
||||
.tcx
|
||||
.associated_item(local_def_id)
|
||||
.trait_item_def_id
|
||||
.and_then(|def_id| def_id.as_local()),
|
||||
// impl items are live if the corresponding traits are live
|
||||
DefKind::Impl { of_trait: true } => self
|
||||
.tcx
|
||||
.impl_trait_ref(impl_id.owner_id.def_id)
|
||||
.and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)),
|
||||
.and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(trait_def_id) = trait_def_id {
|
||||
if let Some(trait_def_id) = trait_def_id.as_local()
|
||||
&& !self.live_symbols.contains(&trait_def_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: legacy logic to check whether the function may construct `Self`,
|
||||
// this can be removed after supporting marking ADTs appearing in patterns
|
||||
// as live, then we can check private impls of public traits directly
|
||||
if let Some(fn_sig) =
|
||||
self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
|
||||
&& matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
|
||||
&& self.tcx.visibility(trait_def_id).is_public()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if let Some(trait_def_id) = trait_def_id
|
||||
&& !self.live_symbols.contains(&trait_def_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The impl or impl item is used if the corresponding trait or trait item is used and the ty is used.
|
||||
|
|
@ -635,6 +608,11 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
|
|||
fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) {
|
||||
match &expr.kind {
|
||||
rustc_hir::PatExprKind::Path(qpath) => {
|
||||
// mark the type of variant live when meeting E::V in expr
|
||||
if let ty::Adt(adt, _) = self.typeck_results().node_type(expr.hir_id).kind() {
|
||||
self.check_def_id(adt.did());
|
||||
}
|
||||
|
||||
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
|
||||
self.handle_res(res);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,6 @@ use crate::ascii::Char as AsciiChar;
|
|||
/// ```
|
||||
#[rustc_diagnostic_item = "Default"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_trivial_field_reads]
|
||||
pub trait Default: Sized {
|
||||
/// Returns the "default value" for a type.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
//@ compile-flags: -Znext-solver
|
||||
//@ check-pass
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub trait Future {
|
||||
type Error;
|
||||
fn poll() -> Self::Error;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub trait Foo {
|
|||
[(); Self::ASSOC_C]:;
|
||||
}
|
||||
|
||||
struct Bar<const N: &'static ()>;
|
||||
struct Bar<const N: &'static ()>; //~ WARN struct `Bar` is never constructed
|
||||
impl<const N: &'static ()> Foo for Bar<N> {
|
||||
const ASSOC_C: usize = 3;
|
||||
|
||||
|
|
|
|||
10
tests/ui/const-generics/issues/issue-86535-2.stderr
Normal file
10
tests/ui/const-generics/issues/issue-86535-2.stderr
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
warning: struct `Bar` is never constructed
|
||||
--> $DIR/issue-86535-2.rs:12:8
|
||||
|
|
||||
LL | struct Bar<const N: &'static ()>;
|
||||
| ^^^
|
||||
|
|
||||
= note: `#[warn(dead_code)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
#![feature(adt_const_params, unsized_const_params, generic_const_exprs)]
|
||||
#![allow(incomplete_features, unused_variables)]
|
||||
|
||||
struct F<const S: &'static str>;
|
||||
struct F<const S: &'static str>; //~ WARN struct `F` is never constructed
|
||||
impl<const S: &'static str> X for F<{ S }> {
|
||||
const W: usize = 3;
|
||||
|
||||
|
|
|
|||
10
tests/ui/const-generics/issues/issue-86535.stderr
Normal file
10
tests/ui/const-generics/issues/issue-86535.stderr
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
warning: struct `F` is never constructed
|
||||
--> $DIR/issue-86535.rs:5:8
|
||||
|
|
||||
LL | struct F<const S: &'static str>;
|
||||
| ^
|
||||
|
|
||||
= note: `#[warn(dead_code)]` on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ LL | struct D { f: () }
|
|||
| |
|
||||
| field in this struct
|
||||
|
|
||||
= note: `D` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis
|
||||
= note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis
|
||||
|
||||
error: field `f` is never read
|
||||
--> $DIR/clone-debug-dead-code.rs:21:12
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//@ run-rustfix
|
||||
|
||||
#![allow(dead_code)]
|
||||
struct S<T>(T);
|
||||
struct S2;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//@ run-rustfix
|
||||
|
||||
#![allow(dead_code)]
|
||||
struct S<T>(T);
|
||||
struct S2;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
error: unexpected `impl` keyword
|
||||
--> $DIR/extra-impl-in-trait-impl.rs:6:18
|
||||
--> $DIR/extra-impl-in-trait-impl.rs:7:18
|
||||
|
|
||||
LL | impl<T: Default> impl Default for S<T> {
|
||||
| ^^^^^ help: remove the extra `impl`
|
||||
|
|
||||
note: this is parsed as an `impl Trait` type, but a trait is expected at this position
|
||||
--> $DIR/extra-impl-in-trait-impl.rs:6:18
|
||||
--> $DIR/extra-impl-in-trait-impl.rs:7:18
|
||||
|
|
||||
LL | impl<T: Default> impl Default for S<T> {
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: unexpected `impl` keyword
|
||||
--> $DIR/extra-impl-in-trait-impl.rs:12:6
|
||||
--> $DIR/extra-impl-in-trait-impl.rs:13:6
|
||||
|
|
||||
LL | impl impl Default for S2 {
|
||||
| ^^^^^ help: remove the extra `impl`
|
||||
|
|
||||
note: this is parsed as an `impl Trait` type, but a trait is expected at this position
|
||||
--> $DIR/extra-impl-in-trait-impl.rs:12:6
|
||||
--> $DIR/extra-impl-in-trait-impl.rs:13:6
|
||||
|
|
||||
LL | impl impl Default for S2 {
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ error: struct `UnusedStruct` is never constructed
|
|||
|
|
||||
LL | struct UnusedStruct;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
#![deny(dead_code)]
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Foo {
|
||||
#[allow(dead_code)]
|
||||
inner: u32,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
#![deny(dead_code)]
|
||||
|
||||
struct Foo(u8); //~ ERROR struct `Foo` is never constructed
|
||||
|
||||
enum Bar { //~ ERROR enum `Bar` is never used
|
||||
Var1(u8),
|
||||
Var2(u8),
|
||||
}
|
||||
|
||||
pub trait Tr1 {
|
||||
fn f1() -> Self;
|
||||
}
|
||||
|
||||
impl Tr1 for Foo {
|
||||
fn f1() -> Foo {
|
||||
let f = Foo(0);
|
||||
let Foo(tag) = f;
|
||||
Foo(tag)
|
||||
}
|
||||
}
|
||||
|
||||
impl Tr1 for Bar {
|
||||
fn f1() -> Bar {
|
||||
let b = Bar::Var1(0);
|
||||
let b = if let Bar::Var1(_) = b {
|
||||
Bar::Var1(0)
|
||||
} else {
|
||||
Bar::Var2(0)
|
||||
};
|
||||
match b {
|
||||
Bar::Var1(_) => Bar::Var2(0),
|
||||
Bar::Var2(_) => Bar::Var1(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
error: struct `Foo` is never constructed
|
||||
--> $DIR/lint-unused-adt-appeared-in-pattern.rs:3:8
|
||||
|
|
||||
LL | struct Foo(u8);
|
||||
| ^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/lint-unused-adt-appeared-in-pattern.rs:1:9
|
||||
|
|
||||
LL | #![deny(dead_code)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: enum `Bar` is never used
|
||||
--> $DIR/lint-unused-adt-appeared-in-pattern.rs:5:6
|
||||
|
|
||||
LL | enum Bar {
|
||||
| ^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
@ -56,8 +56,6 @@ warning: struct `Foo` is never constructed
|
|||
|
|
||||
LL | struct Foo(usize, #[allow(unused)] usize);
|
||||
| ^^^
|
||||
|
|
||||
= note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
|
||||
|
||||
error: aborting due to 2 previous errors; 2 warnings emitted
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
//@ check-pass
|
||||
|
||||
#![deny(dead_code)]
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum RecordField {
|
||||
Target = 1,
|
||||
Level,
|
||||
Module,
|
||||
File,
|
||||
Line,
|
||||
NumArgs,
|
||||
}
|
||||
|
||||
unsafe trait Pod {}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct RecordFieldWrapper(RecordField);
|
||||
|
||||
unsafe impl Pod for RecordFieldWrapper {}
|
||||
|
||||
fn try_read<T: Pod>(buf: &[u8]) -> T {
|
||||
unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) }
|
||||
}
|
||||
|
||||
pub fn foo(buf: &[u8]) -> RecordField {
|
||||
let RecordFieldWrapper(tag) = try_read(buf);
|
||||
tag
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
struct T1; //~ ERROR struct `T1` is never constructed
|
||||
pub struct T2(i32); //~ ERROR field `0` is never read
|
||||
struct T3;
|
||||
struct T3; //~ ERROR struct `T3` is never constructed
|
||||
|
||||
trait Trait1 { //~ ERROR trait `Trait1` is never used
|
||||
const UNUSED: i32;
|
||||
|
|
|
|||
|
|
@ -20,11 +20,17 @@ LL | pub struct T2(i32);
|
|||
|
|
||||
= help: consider removing this field
|
||||
|
||||
error: struct `T3` is never constructed
|
||||
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:5:8
|
||||
|
|
||||
LL | struct T3;
|
||||
| ^^
|
||||
|
||||
error: trait `Trait1` is never used
|
||||
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7
|
||||
|
|
||||
LL | trait Trait1 {
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -22,4 +22,5 @@ pub struct T2 {
|
|||
|
||||
fn main() {
|
||||
let _x: Used = Default::default();
|
||||
let _e: E = Default::default();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ error: struct `T` is never constructed
|
|||
LL | struct T;
|
||||
| ^
|
||||
|
|
||||
= note: `T` has a derived impl for the trait `Default`, but this is intentionally ignored during dead code analysis
|
||||
note: the lint level is defined here
|
||||
--> $DIR/unused-struct-derive-default.rs:1:9
|
||||
|
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//@ run-rustfix
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Foo;
|
||||
|
||||
impl From<i32> for Foo {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//@ run-rustfix
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Foo;
|
||||
|
||||
fn From<i32> for Foo {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
error: you might have meant to write `impl` instead of `fn`
|
||||
--> $DIR/issue-105366.rs:5:1
|
||||
--> $DIR/issue-105366.rs:6:1
|
||||
|
|
||||
LL | fn From<i32> for Foo {
|
||||
| ^^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue