diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 0563e9619419b..3294b6802a719 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -778,6 +778,15 @@ fn maybe_record_as_seed<'tcx>( match tcx.def_kind(parent) { DefKind::Impl { of_trait: false } | DefKind::Trait => {} DefKind::Impl { of_trait: true } => { + if let Some(trait_item_def_id) = + tcx.associated_item(owner_id.def_id).trait_item_def_id() + && let Some(trait_item_local_def_id) = trait_item_def_id.as_local() + && let Some(comes_from_allow) = + has_allow_dead_code_or_lang_attr(tcx, trait_item_local_def_id) + { + worklist.push((owner_id.def_id, comes_from_allow)); + } + // We only care about associated items of traits, // because they cannot be visited directly, // so we later mark them as live if their corresponding traits @@ -791,6 +800,14 @@ fn maybe_record_as_seed<'tcx>( } DefKind::Impl { of_trait: true } => { if allow_dead_code.is_none() { + if let Some(trait_def_id) = + tcx.impl_trait_ref(owner_id.def_id).skip_binder().def_id.as_local() + && let Some(comes_from_allow) = + has_allow_dead_code_or_lang_attr(tcx, trait_def_id) + { + worklist.push((owner_id.def_id, comes_from_allow)); + } + unsolved_items.push(owner_id.def_id); } } diff --git a/tests/ui/lint/dead-code/allow-trait-or-impl.rs b/tests/ui/lint/dead-code/allow-trait-or-impl.rs new file mode 100644 index 0000000000000..92817549a91e3 --- /dev/null +++ b/tests/ui/lint/dead-code/allow-trait-or-impl.rs @@ -0,0 +1,38 @@ +#![deny(dead_code)] + +pub mod a { + pub trait Foo { } + impl Foo for u32 { } + + struct PrivateType; //~ ERROR struct `PrivateType` is never constructed + impl Foo for PrivateType { } // <-- warns as dead, even though Foo is public + + struct AnotherPrivateType; //~ ERROR struct `AnotherPrivateType` is never constructed + impl Foo for AnotherPrivateType { } // <-- warns as dead, even though Foo is public +} + +pub mod b { + #[allow(dead_code)] + pub trait Foo { } + impl Foo for u32 { } + + struct PrivateType; + impl Foo for PrivateType { } // <-- no warning, trait is "allowed" + + struct AnotherPrivateType; + impl Foo for AnotherPrivateType { } // <-- no warning, trait is "allowed" +} + +pub mod c { + pub trait Foo { } + impl Foo for u32 { } + + struct PrivateType; + #[allow(dead_code)] + impl Foo for PrivateType { } // <-- no warning, impl is allowed + + struct AnotherPrivateType; //~ ERROR struct `AnotherPrivateType` is never constructed + impl Foo for AnotherPrivateType { } // <-- warns as dead, even though Foo is public +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/allow-trait-or-impl.stderr b/tests/ui/lint/dead-code/allow-trait-or-impl.stderr new file mode 100644 index 0000000000000..130116b6c91d0 --- /dev/null +++ b/tests/ui/lint/dead-code/allow-trait-or-impl.stderr @@ -0,0 +1,26 @@ +error: struct `PrivateType` is never constructed + --> $DIR/allow-trait-or-impl.rs:7:12 + | +LL | struct PrivateType; + | ^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/allow-trait-or-impl.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: struct `AnotherPrivateType` is never constructed + --> $DIR/allow-trait-or-impl.rs:10:12 + | +LL | struct AnotherPrivateType; + | ^^^^^^^^^^^^^^^^^^ + +error: struct `AnotherPrivateType` is never constructed + --> $DIR/allow-trait-or-impl.rs:34:12 + | +LL | struct AnotherPrivateType; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/dead-code/allow-unused-trait.rs b/tests/ui/lint/dead-code/allow-unused-trait.rs new file mode 100644 index 0000000000000..4eb63bd4d27ab --- /dev/null +++ b/tests/ui/lint/dead-code/allow-unused-trait.rs @@ -0,0 +1,29 @@ +//@ check-pass + +#![deny(dead_code)] + +#[allow(dead_code)] +trait Foo { + const FOO: u32; + type Baz; + fn foobar(); +} + +const fn bar(x: u32) -> u32 { + x +} + +struct Qux; + +struct FooBar; + +impl Foo for u32 { + const FOO: u32 = bar(0); + type Baz = Qux; + + fn foobar() { + let _ = FooBar; + } +} + +fn main() {}