about summary refs log tree commit diff
diff options
context:
space:
mode:
authorr0cky <mu001999@outlook.com>2024-06-05 23:20:09 +0800
committerr0cky <mu001999@outlook.com>2024-06-05 23:20:09 +0800
commit35130d7233e939cc9a6fd8b72a4baee2eb59c3b2 (patch)
treeea13e12817d949301828e9ff4f5775ca8ba9a31e
parent2a2c29aafa50bf6fe53d66b32070eba59f860ac3 (diff)
downloadrust-35130d7233e939cc9a6fd8b72a4baee2eb59c3b2.tar.gz
rust-35130d7233e939cc9a6fd8b72a4baee2eb59c3b2.zip
Detect pub structs never constructed and unused associated constants in traits
-rw-r--r--compiler/rustc_passes/src/dead.rs121
-rw-r--r--tests/codegen-units/item-collection/generic-impl.rs10
-rw-r--r--tests/codegen-units/item-collection/overloaded-operators.rs24
-rw-r--r--tests/ui/coherence/re-rebalance-coherence.rs1
-rw-r--r--tests/ui/const-generics/defaults/repr-c-issue-82792.rs1
-rw-r--r--tests/ui/const-generics/generic_const_exprs/associated-consts.rs3
-rw-r--r--tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs1
-rw-r--r--tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs4
-rw-r--r--tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr16
-rw-r--r--tests/ui/issues/issue-5708.rs1
-rw-r--r--tests/ui/lint/dead-code/lint-dead-code-1.rs5
-rw-r--r--tests/ui/lint/dead-code/lint-dead-code-1.stderr26
-rw-r--r--tests/ui/lint/dead-code/unused-assoc-const.rs20
-rw-r--r--tests/ui/lint/dead-code/unused-assoc-const.stderr16
-rw-r--r--tests/ui/lint/dead-code/unused-pub-struct.rs48
-rw-r--r--tests/ui/lint/dead-code/unused-pub-struct.stderr14
-rw-r--r--tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed3
-rw-r--r--tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs3
-rw-r--r--tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr10
-rw-r--r--tests/ui/pub/pub-ident-struct-4.fixed3
-rw-r--r--tests/ui/pub/pub-ident-struct-4.rs3
-rw-r--r--tests/ui/pub/pub-ident-struct-4.stderr6
-rw-r--r--tests/ui/regions/regions-issue-21422.rs1
-rw-r--r--tests/ui/structs-enums/newtype-struct-with-dtor.rs2
-rw-r--r--tests/ui/structs-enums/uninstantiable-struct.rs3
-rw-r--r--tests/ui/suggestions/derive-clone-for-eq.fixed1
-rw-r--r--tests/ui/suggestions/derive-clone-for-eq.rs1
-rw-r--r--tests/ui/suggestions/derive-clone-for-eq.stderr4
-rw-r--r--tests/ui/suggestions/option-content-move.fixed2
-rw-r--r--tests/ui/suggestions/option-content-move.rs2
-rw-r--r--tests/ui/suggestions/option-content-move.stderr4
-rw-r--r--tests/ui/traits/object/generics.rs1
32 files changed, 272 insertions, 88 deletions
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 0049afff528..4c2ea76ff65 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -15,7 +15,7 @@ use rustc_hir::{Node, PatKind, TyKind};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::privacy::Level;
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, AssocItemContainer, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
 use rustc_session::lint::builtin::DEAD_CODE;
@@ -44,16 +44,63 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
     )
 }
 
-fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool {
+struct Publicness {
+    ty_is_public: bool,
+    ty_and_all_fields_are_public: bool,
+}
+
+impl Publicness {
+    fn new(ty_is_public: bool, ty_and_all_fields_are_public: bool) -> Self {
+        Self { ty_is_public, ty_and_all_fields_are_public }
+    }
+}
+
+fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
+    // treat PhantomData and positional ZST as public,
+    // we don't want to lint types which only have them,
+    // cause it's a common way to use such types to check things like well-formedness
+    tcx.adt_def(id).all_fields().all(|field| {
+        let field_type = tcx.type_of(field.did).instantiate_identity();
+        if field_type.is_phantom_data() {
+            return true;
+        }
+        let is_positional = field.name.as_str().starts_with(|c: char| c.is_ascii_digit());
+        if is_positional
+            && tcx
+                .layout_of(tcx.param_env(field.did).and(field_type))
+                .map_or(true, |layout| layout.is_zst())
+        {
+            return true;
+        }
+        field.vis.is_public()
+    })
+}
+
+/// check struct and its fields are public or not,
+/// for enum and union, just check they are public,
+/// and doesn't solve types like &T for now, just skip them
+fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness {
     if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
         && let Res::Def(def_kind, def_id) = path.res
         && def_id.is_local()
-        && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
     {
-        tcx.visibility(def_id).is_public()
-    } else {
-        true
+        return match def_kind {
+            DefKind::Enum | DefKind::Union => {
+                let ty_is_public = tcx.visibility(def_id).is_public();
+                Publicness::new(ty_is_public, ty_is_public)
+            }
+            DefKind::Struct => {
+                let ty_is_public = tcx.visibility(def_id).is_public();
+                Publicness::new(
+                    ty_is_public,
+                    ty_is_public && struct_all_fields_are_public(tcx, def_id),
+                )
+            }
+            _ => Publicness::new(true, true),
+        };
     }
+
+    Publicness::new(true, true)
 }
 
 /// Determine if a work from the worklist is coming from the a `#[allow]`
@@ -427,9 +474,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                         {
                             if matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
                                 && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
+                                    .ty_and_all_fields_are_public
                             {
-                                // skip methods of private ty,
-                                // they would be solved in `solve_rest_impl_items`
+                                // skip impl-items of non pure pub ty,
+                                // cause we don't know the ty is constructed or not,
+                                // check these later in `solve_rest_impl_items`
                                 continue;
                             }
 
@@ -510,22 +559,21 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
             && let Some(local_def_id) = def_id.as_local()
             && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
         {
-            if self.tcx.visibility(impl_item_id).is_public() {
-                // for the public method, we don't know the trait item is used or not,
-                // so we mark the method live if the self is used
-                return self.live_symbols.contains(&local_def_id);
-            }
-
             if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
                 && let Some(local_id) = trait_item_id.as_local()
             {
-                // for the private method, we can know the trait item is used or not,
+                // for the local impl item, we can know the trait item is used or not,
                 // so we mark the method live if the self is used and the trait item is used
-                return self.live_symbols.contains(&local_id)
-                    && self.live_symbols.contains(&local_def_id);
+                self.live_symbols.contains(&local_id) && self.live_symbols.contains(&local_def_id)
+            } else {
+                // for the foreign method and inherent pub method,
+                // we don't know the trait item or the method is used or not,
+                // so we mark the method live if the self is used
+                self.live_symbols.contains(&local_def_id)
             }
+        } else {
+            false
         }
-        false
     }
 }
 
@@ -746,7 +794,9 @@ fn check_item<'tcx>(
                 .iter()
                 .filter_map(|def_id| def_id.as_local());
 
-            let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir().item(id).expect_impl().self_ty);
+            let self_ty = tcx.hir().item(id).expect_impl().self_ty;
+            let Publicness { ty_is_public, ty_and_all_fields_are_public } =
+                ty_ref_to_pub_struct(tcx, self_ty);
 
             // And we access the Map here to get HirId from LocalDefId
             for local_def_id in local_def_ids {
@@ -762,18 +812,20 @@ fn check_item<'tcx>(
                 // for trait impl blocks,
                 // mark the method live if the self_ty is public,
                 // or the method is public and may construct self
-                if of_trait
-                    && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
-                        || tcx.visibility(local_def_id).is_public()
-                            && (ty_is_pub || may_construct_self))
+                if of_trait && matches!(tcx.def_kind(local_def_id), DefKind::AssocTy)
+                    || tcx.visibility(local_def_id).is_public()
+                        && (ty_and_all_fields_are_public || may_construct_self)
                 {
+                    // if the impl item is public,
+                    // and the ty may be constructed or can be constructed in foreign crates,
+                    // mark the impl item live
                     worklist.push((local_def_id, ComesFromAllowExpect::No));
                 } else if let Some(comes_from_allow) =
                     has_allow_dead_code_or_lang_attr(tcx, local_def_id)
                 {
                     worklist.push((local_def_id, comes_from_allow));
-                } else if of_trait {
-                    // private method || public method not constructs self
+                } else if of_trait || tcx.visibility(local_def_id).is_public() && ty_is_public {
+                    // private impl items of traits || public impl items not constructs self
                     unsolved_impl_items.push((id, local_def_id));
                 }
             }
@@ -840,6 +892,14 @@ fn create_and_seed_worklist(
             effective_vis
                 .is_public_at_level(Level::Reachable)
                 .then_some(id)
+                .filter(|&id|
+                    // checks impls, impl-items and pub structs with all public fields later
+                    match tcx.def_kind(id) {
+                        DefKind::Impl { .. } => false,
+                        DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
+                        DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()),
+                        _ => true
+                    })
                 .map(|id| (id, ComesFromAllowExpect::No))
         })
         // Seed entry point
@@ -1112,10 +1172,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
             || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
         {
             for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
-                // We have diagnosed unused methods in traits
+                // We have diagnosed unused assoc consts and fns in traits
                 if matches!(def_kind, DefKind::Impl { of_trait: true })
-                    && tcx.def_kind(def_id) == DefKind::AssocFn
-                    || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
+                    && matches!(tcx.def_kind(def_id), DefKind::AssocConst | DefKind::AssocFn)
+                    // skip unused public inherent methods,
+                    // cause we have diagnosed unconstructed struct
+                    || matches!(def_kind, DefKind::Impl { of_trait: false })
+                        && tcx.visibility(def_id).is_public()
+                        && ty_ref_to_pub_struct(tcx, tcx.hir().item(item).expect_impl().self_ty).ty_is_public
+                    || def_kind == DefKind::Trait && tcx.def_kind(def_id) == DefKind::AssocTy
                 {
                     continue;
                 }
diff --git a/tests/codegen-units/item-collection/generic-impl.rs b/tests/codegen-units/item-collection/generic-impl.rs
index 23d09e0d8af..b4cd99272b1 100644
--- a/tests/codegen-units/item-collection/generic-impl.rs
+++ b/tests/codegen-units/item-collection/generic-impl.rs
@@ -22,16 +22,16 @@ impl<T> Struct<T> {
     }
 }
 
-pub struct LifeTimeOnly<'a> {
+pub struct _LifeTimeOnly<'a> {
     _a: &'a u32,
 }
 
-impl<'a> LifeTimeOnly<'a> {
-    //~ MONO_ITEM fn LifeTimeOnly::<'_>::foo
+impl<'a> _LifeTimeOnly<'a> {
+    //~ MONO_ITEM fn _LifeTimeOnly::<'_>::foo
     pub fn foo(&self) {}
-    //~ MONO_ITEM fn LifeTimeOnly::<'_>::bar
+    //~ MONO_ITEM fn _LifeTimeOnly::<'_>::bar
     pub fn bar(&'a self) {}
-    //~ MONO_ITEM fn LifeTimeOnly::<'_>::baz
+    //~ MONO_ITEM fn _LifeTimeOnly::<'_>::baz
     pub fn baz<'b>(&'b self) {}
 
     pub fn non_instantiated<T>(&self) {}
diff --git a/tests/codegen-units/item-collection/overloaded-operators.rs b/tests/codegen-units/item-collection/overloaded-operators.rs
index 69b55695d3d..e00e22dbab9 100644
--- a/tests/codegen-units/item-collection/overloaded-operators.rs
+++ b/tests/codegen-units/item-collection/overloaded-operators.rs
@@ -5,44 +5,44 @@
 
 use std::ops::{Add, Deref, Index, IndexMut};
 
-pub struct Indexable {
+pub struct _Indexable {
     data: [u8; 3],
 }
 
-impl Index<usize> for Indexable {
+impl Index<usize> for _Indexable {
     type Output = u8;
 
-    //~ MONO_ITEM fn <Indexable as std::ops::Index<usize>>::index
+    //~ MONO_ITEM fn <_Indexable as std::ops::Index<usize>>::index
     fn index(&self, index: usize) -> &Self::Output {
         if index >= 3 { &self.data[0] } else { &self.data[index] }
     }
 }
 
-impl IndexMut<usize> for Indexable {
-    //~ MONO_ITEM fn <Indexable as std::ops::IndexMut<usize>>::index_mut
+impl IndexMut<usize> for _Indexable {
+    //~ MONO_ITEM fn <_Indexable as std::ops::IndexMut<usize>>::index_mut
     fn index_mut(&mut self, index: usize) -> &mut Self::Output {
         if index >= 3 { &mut self.data[0] } else { &mut self.data[index] }
     }
 }
 
-//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::eq
-//~ MONO_ITEM fn <Equatable as std::cmp::PartialEq>::ne
+//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::eq
+//~ MONO_ITEM fn <_Equatable as std::cmp::PartialEq>::ne
 #[derive(PartialEq)]
-pub struct Equatable(u32);
+pub struct _Equatable(u32);
 
-impl Add<u32> for Equatable {
+impl Add<u32> for _Equatable {
     type Output = u32;
 
-    //~ MONO_ITEM fn <Equatable as std::ops::Add<u32>>::add
+    //~ MONO_ITEM fn <_Equatable as std::ops::Add<u32>>::add
     fn add(self, rhs: u32) -> u32 {
         self.0 + rhs
     }
 }
 
-impl Deref for Equatable {
+impl Deref for _Equatable {
     type Target = u32;
 
-    //~ MONO_ITEM fn <Equatable as std::ops::Deref>::deref
+    //~ MONO_ITEM fn <_Equatable as std::ops::Deref>::deref
     fn deref(&self) -> &Self::Target {
         &self.0
     }
diff --git a/tests/ui/coherence/re-rebalance-coherence.rs b/tests/ui/coherence/re-rebalance-coherence.rs
index 9c176d5b1b1..5383a634617 100644
--- a/tests/ui/coherence/re-rebalance-coherence.rs
+++ b/tests/ui/coherence/re-rebalance-coherence.rs
@@ -4,6 +4,7 @@
 extern crate re_rebalance_coherence_lib as lib;
 use lib::*;
 
+#[allow(dead_code)]
 struct Oracle;
 impl Backend for Oracle {}
 impl<'a, T:'a, Tab> QueryFragment<Oracle> for BatchInsert<'a, T, Tab> {}
diff --git a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs
index c23187598bc..4bf2fa761ea 100644
--- a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs
+++ b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs
@@ -2,6 +2,7 @@
 
 //@ run-pass
 
+#[allow(dead_code)]
 #[repr(C)]
 pub struct Loaf<T: Sized, const N: usize = 1> {
     head: [T; N],
diff --git a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs
index 5d2198f50ad..50a6102c605 100644
--- a/tests/ui/const-generics/generic_const_exprs/associated-consts.rs
+++ b/tests/ui/const-generics/generic_const_exprs/associated-consts.rs
@@ -16,7 +16,8 @@ impl BlockCipher for BarCipher {
     const BLOCK_SIZE: usize = 32;
 }
 
-pub struct Block<C>(#[allow(dead_code)] C);
+#[allow(dead_code)]
+pub struct Block<C>(C);
 
 pub fn test<C: BlockCipher, const M: usize>()
 where
diff --git a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs
index 419d605d0c8..35c41ae4615 100644
--- a/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs
+++ b/tests/ui/const-generics/transparent-maybeunit-array-wrapper.rs
@@ -6,6 +6,7 @@
 
 use std::mem::MaybeUninit;
 
+#[allow(dead_code)]
 #[repr(transparent)]
 pub struct MaybeUninitWrapper<const N: usize>(MaybeUninit<[u64; N]>);
 
diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs
index 6ab1fb7b039..885dacc727a 100644
--- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs
+++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.rs
@@ -1,9 +1,9 @@
 #![forbid(dead_code)]
 
 #[derive(Debug)]
-pub struct Whatever {
+pub struct Whatever { //~ ERROR struct `Whatever` is never constructed
     pub field0: (),
-    field1: (), //~ ERROR fields `field1`, `field2`, `field3`, and `field4` are never read
+    field1: (),
     field2: (),
     field3: (),
     field4: (),
diff --git a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr
index e9b757b6bae..e10d28ad03a 100644
--- a/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr
+++ b/tests/ui/derives/clone-debug-dead-code-in-the-same-struct.stderr
@@ -1,19 +1,9 @@
-error: fields `field1`, `field2`, `field3`, and `field4` are never read
-  --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:6:5
+error: struct `Whatever` is never constructed
+  --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:4:12
    |
 LL | pub struct Whatever {
-   |            -------- fields in this struct
-LL |     pub field0: (),
-LL |     field1: (),
-   |     ^^^^^^
-LL |     field2: (),
-   |     ^^^^^^
-LL |     field3: (),
-   |     ^^^^^^
-LL |     field4: (),
-   |     ^^^^^^
+   |            ^^^^^^^^
    |
-   = note: `Whatever` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
 note: the lint level is defined here
   --> $DIR/clone-debug-dead-code-in-the-same-struct.rs:1:11
    |
diff --git a/tests/ui/issues/issue-5708.rs b/tests/ui/issues/issue-5708.rs
index ce9ef78ffcd..89ea9fbdcd8 100644
--- a/tests/ui/issues/issue-5708.rs
+++ b/tests/ui/issues/issue-5708.rs
@@ -44,6 +44,7 @@ pub trait MyTrait<T> {
     fn dummy(&self, t: T) -> T { panic!() }
 }
 
+#[allow(dead_code)]
 pub struct MyContainer<'a, T:'a> {
     foos: Vec<&'a (dyn MyTrait<T>+'a)> ,
 }
diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs
index ddcafedf7bc..3386dfa4747 100644
--- a/tests/ui/lint/dead-code/lint-dead-code-1.rs
+++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs
@@ -46,11 +46,10 @@ struct SemiUsedStruct;
 impl SemiUsedStruct {
     fn la_la_la() {}
 }
-struct StructUsedAsField;
+struct StructUsedAsField; //~ ERROR struct `StructUsedAsField` is never constructed
 pub struct StructUsedInEnum;
 struct StructUsedInGeneric;
-pub struct PubStruct2 {
-    #[allow(dead_code)]
+pub struct PubStruct2 { //~ ERROR struct `PubStruct2` is never constructed
     struct_used_as_field: *const StructUsedAsField
 }
 
diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr
index eb728b5b930..b0163df8855 100644
--- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr
+++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr
@@ -22,14 +22,26 @@ error: struct `PrivStruct` is never constructed
 LL | struct PrivStruct;
    |        ^^^^^^^^^^
 
+error: struct `StructUsedAsField` is never constructed
+  --> $DIR/lint-dead-code-1.rs:49:8
+   |
+LL | struct StructUsedAsField;
+   |        ^^^^^^^^^^^^^^^^^
+
+error: struct `PubStruct2` is never constructed
+  --> $DIR/lint-dead-code-1.rs:52:12
+   |
+LL | pub struct PubStruct2 {
+   |            ^^^^^^^^^^
+
 error: enum `priv_enum` is never used
-  --> $DIR/lint-dead-code-1.rs:64:6
+  --> $DIR/lint-dead-code-1.rs:63:6
    |
 LL | enum priv_enum { foo2, bar2 }
    |      ^^^^^^^^^
 
 error: variant `bar3` is never constructed
-  --> $DIR/lint-dead-code-1.rs:67:5
+  --> $DIR/lint-dead-code-1.rs:66:5
    |
 LL | enum used_enum {
    |      --------- variant in this enum
@@ -38,25 +50,25 @@ LL |     bar3
    |     ^^^^
 
 error: function `priv_fn` is never used
-  --> $DIR/lint-dead-code-1.rs:88:4
+  --> $DIR/lint-dead-code-1.rs:87:4
    |
 LL | fn priv_fn() {
    |    ^^^^^^^
 
 error: function `foo` is never used
-  --> $DIR/lint-dead-code-1.rs:93:4
+  --> $DIR/lint-dead-code-1.rs:92:4
    |
 LL | fn foo() {
    |    ^^^
 
 error: function `bar` is never used
-  --> $DIR/lint-dead-code-1.rs:98:4
+  --> $DIR/lint-dead-code-1.rs:97:4
    |
 LL | fn bar() {
    |    ^^^
 
 error: function `baz` is never used
-  --> $DIR/lint-dead-code-1.rs:102:4
+  --> $DIR/lint-dead-code-1.rs:101:4
    |
 LL | fn baz() -> impl Copy {
    |    ^^^
@@ -67,5 +79,5 @@ error: struct `Bar` is never constructed
 LL |     pub struct Bar;
    |                ^^^
 
-error: aborting due to 10 previous errors
+error: aborting due to 12 previous errors
 
diff --git a/tests/ui/lint/dead-code/unused-assoc-const.rs b/tests/ui/lint/dead-code/unused-assoc-const.rs
new file mode 100644
index 00000000000..36e8315ad36
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-assoc-const.rs
@@ -0,0 +1,20 @@
+#![deny(dead_code)]
+
+trait Trait {
+    const UNUSED_CONST: i32; //~ ERROR associated constant `UNUSED_CONST` is never used
+    const USED_CONST: i32;
+
+    fn foo(&self) {}
+}
+
+pub struct T(());
+
+impl Trait for T {
+    const UNUSED_CONST: i32 = 0;
+    const USED_CONST: i32 = 1;
+}
+
+fn main() {
+    T(()).foo();
+    T::USED_CONST;
+}
diff --git a/tests/ui/lint/dead-code/unused-assoc-const.stderr b/tests/ui/lint/dead-code/unused-assoc-const.stderr
new file mode 100644
index 00000000000..78296d70663
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-assoc-const.stderr
@@ -0,0 +1,16 @@
+error: associated constant `UNUSED_CONST` is never used
+  --> $DIR/unused-assoc-const.rs:4:11
+   |
+LL | trait Trait {
+   |       ----- associated constant in this trait
+LL |     const UNUSED_CONST: i32;
+   |           ^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-assoc-const.rs:1:9
+   |
+LL | #![deny(dead_code)]
+   |         ^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/lint/dead-code/unused-pub-struct.rs b/tests/ui/lint/dead-code/unused-pub-struct.rs
new file mode 100644
index 00000000000..aaf4dd612de
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-pub-struct.rs
@@ -0,0 +1,48 @@
+#![deny(dead_code)]
+
+pub struct NotLint1(());
+pub struct NotLint2(std::marker::PhantomData<i32>);
+
+pub struct NeverConstructed(i32); //~ ERROR struct `NeverConstructed` is never constructed
+
+impl NeverConstructed {
+    pub fn not_construct_self(&self) {}
+}
+
+impl Clone for NeverConstructed {
+    fn clone(&self) -> NeverConstructed {
+        NeverConstructed(0)
+    }
+}
+
+pub trait Trait {
+    fn not_construct_self(&self);
+}
+
+impl Trait for NeverConstructed {
+    fn not_construct_self(&self) {
+        self.0;
+    }
+}
+
+pub struct Constructed(i32);
+
+impl Constructed {
+    pub fn construct_self() -> Self {
+        Constructed(0)
+    }
+}
+
+impl Clone for Constructed {
+    fn clone(&self) -> Constructed {
+        Constructed(0)
+    }
+}
+
+impl Trait for Constructed {
+    fn not_construct_self(&self) {
+        self.0;
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/lint/dead-code/unused-pub-struct.stderr b/tests/ui/lint/dead-code/unused-pub-struct.stderr
new file mode 100644
index 00000000000..3667ddb97bd
--- /dev/null
+++ b/tests/ui/lint/dead-code/unused-pub-struct.stderr
@@ -0,0 +1,14 @@
+error: struct `NeverConstructed` is never constructed
+  --> $DIR/unused-pub-struct.rs:6:12
+   |
+LL | pub struct NeverConstructed(i32);
+   |            ^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unused-pub-struct.rs:1:9
+   |
+LL | #![deny(dead_code)]
+   |         ^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed
index a851300a982..e9c89807fa5 100644
--- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed
+++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.fixed
@@ -1,7 +1,8 @@
 // Regression test for issues #100790 and #106439.
 //@ run-rustfix
 
-pub struct Example(#[allow(dead_code)] usize)
+#[allow(dead_code)]
+pub struct Example(usize)
 where
     (): Sized;
 //~^^^ ERROR where clauses are not allowed before tuple struct bodies
diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs
index 10f435859f1..3bd0f51ec2c 100644
--- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs
+++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.rs
@@ -1,10 +1,11 @@
 // Regression test for issues #100790 and #106439.
 //@ run-rustfix
 
+#[allow(dead_code)]
 pub struct Example
 where
     (): Sized,
-(#[allow(dead_code)] usize);
+(usize);
 //~^^^ ERROR where clauses are not allowed before tuple struct bodies
 
 struct _Demo
diff --git a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr
index ddbf237e866..77eafa6bea3 100644
--- a/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr
+++ b/tests/ui/parser/recover/recover-where-clause-before-tuple-struct-body-0.stderr
@@ -1,23 +1,23 @@
 error: where clauses are not allowed before tuple struct bodies
-  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:5:1
+  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:6:1
    |
 LL |   pub struct Example
    |              ------- while parsing this tuple struct
 LL | / where
 LL | |     (): Sized,
    | |______________^ unexpected where clause
-LL |   (#[allow(dead_code)] usize);
-   |   --------------------------- the struct body
+LL |   (usize);
+   |   ------- the struct body
    |
 help: move the body before the where clause
    |
-LL ~ pub struct Example(#[allow(dead_code)] usize)
+LL ~ pub struct Example(usize)
 LL | where
 LL ~     (): Sized;
    |
 
 error: where clauses are not allowed before tuple struct bodies
-  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:11:1
+  --> $DIR/recover-where-clause-before-tuple-struct-body-0.rs:12:1
    |
 LL |   struct _Demo
    |          ----- while parsing this tuple struct
diff --git a/tests/ui/pub/pub-ident-struct-4.fixed b/tests/ui/pub/pub-ident-struct-4.fixed
index 5fedbb72437..a62ece43ece 100644
--- a/tests/ui/pub/pub-ident-struct-4.fixed
+++ b/tests/ui/pub/pub-ident-struct-4.fixed
@@ -1,6 +1,7 @@
 //@ run-rustfix
 
-pub struct T(#[allow(dead_code)] String);
+#[allow(dead_code)]
+pub struct T(String);
 //~^ ERROR missing `struct` for struct definition
 
 fn main() {}
diff --git a/tests/ui/pub/pub-ident-struct-4.rs b/tests/ui/pub/pub-ident-struct-4.rs
index 5c721c25a78..0d56a31beaf 100644
--- a/tests/ui/pub/pub-ident-struct-4.rs
+++ b/tests/ui/pub/pub-ident-struct-4.rs
@@ -1,6 +1,7 @@
 //@ run-rustfix
 
-pub T(#[allow(dead_code)] String);
+#[allow(dead_code)]
+pub T(String);
 //~^ ERROR missing `struct` for struct definition
 
 fn main() {}
diff --git a/tests/ui/pub/pub-ident-struct-4.stderr b/tests/ui/pub/pub-ident-struct-4.stderr
index 5fbb02c8673..ec136783211 100644
--- a/tests/ui/pub/pub-ident-struct-4.stderr
+++ b/tests/ui/pub/pub-ident-struct-4.stderr
@@ -1,12 +1,12 @@
 error: missing `struct` for struct definition
-  --> $DIR/pub-ident-struct-4.rs:3:4
+  --> $DIR/pub-ident-struct-4.rs:4:4
    |
-LL | pub T(#[allow(dead_code)] String);
+LL | pub T(String);
    |    ^
    |
 help: add `struct` here to parse `T` as a public struct
    |
-LL | pub struct T(#[allow(dead_code)] String);
+LL | pub struct T(String);
    |     ++++++
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/regions/regions-issue-21422.rs b/tests/ui/regions/regions-issue-21422.rs
index 54beed9b3ac..67852a6f5de 100644
--- a/tests/ui/regions/regions-issue-21422.rs
+++ b/tests/ui/regions/regions-issue-21422.rs
@@ -5,6 +5,7 @@
 
 //@ pretty-expanded FIXME #23616
 
+#[allow(dead_code)]
 pub struct P<'a> {
     _ptr: *const &'a u8,
 }
diff --git a/tests/ui/structs-enums/newtype-struct-with-dtor.rs b/tests/ui/structs-enums/newtype-struct-with-dtor.rs
index 19672e41c9a..16439a7fedd 100644
--- a/tests/ui/structs-enums/newtype-struct-with-dtor.rs
+++ b/tests/ui/structs-enums/newtype-struct-with-dtor.rs
@@ -3,8 +3,10 @@
 #![allow(unused_variables)]
 //@ pretty-expanded FIXME #23616
 
+#[allow(dead_code)]
 pub struct Fd(u32);
 
+#[allow(dead_code)]
 fn foo(a: u32) {}
 
 impl Drop for Fd {
diff --git a/tests/ui/structs-enums/uninstantiable-struct.rs b/tests/ui/structs-enums/uninstantiable-struct.rs
index 97bc7d8414e..1074dbcd6e6 100644
--- a/tests/ui/structs-enums/uninstantiable-struct.rs
+++ b/tests/ui/structs-enums/uninstantiable-struct.rs
@@ -1,4 +1,5 @@
 //@ run-pass
-pub struct Z(#[allow(dead_code)] &'static Z);
+#[allow(dead_code)]
+pub struct Z(&'static Z);
 
 pub fn main() {}
diff --git a/tests/ui/suggestions/derive-clone-for-eq.fixed b/tests/ui/suggestions/derive-clone-for-eq.fixed
index 4dc362f9478..cf800c6e47d 100644
--- a/tests/ui/suggestions/derive-clone-for-eq.fixed
+++ b/tests/ui/suggestions/derive-clone-for-eq.fixed
@@ -1,6 +1,7 @@
 //@ run-rustfix
 // https://github.com/rust-lang/rust/issues/79076
 
+#[allow(dead_code)]
 #[derive(Clone, Eq)] //~ ERROR [E0277]
 pub struct Struct<T: std::clone::Clone>(T);
 
diff --git a/tests/ui/suggestions/derive-clone-for-eq.rs b/tests/ui/suggestions/derive-clone-for-eq.rs
index b3635000f16..84736426bac 100644
--- a/tests/ui/suggestions/derive-clone-for-eq.rs
+++ b/tests/ui/suggestions/derive-clone-for-eq.rs
@@ -1,6 +1,7 @@
 //@ run-rustfix
 // https://github.com/rust-lang/rust/issues/79076
 
+#[allow(dead_code)]
 #[derive(Clone, Eq)] //~ ERROR [E0277]
 pub struct Struct<T>(T);
 
diff --git a/tests/ui/suggestions/derive-clone-for-eq.stderr b/tests/ui/suggestions/derive-clone-for-eq.stderr
index 6fae6e1316d..54670fbffcf 100644
--- a/tests/ui/suggestions/derive-clone-for-eq.stderr
+++ b/tests/ui/suggestions/derive-clone-for-eq.stderr
@@ -1,11 +1,11 @@
 error[E0277]: the trait bound `T: Clone` is not satisfied
-  --> $DIR/derive-clone-for-eq.rs:4:17
+  --> $DIR/derive-clone-for-eq.rs:5:17
    |
 LL | #[derive(Clone, Eq)]
    |                 ^^ the trait `Clone` is not implemented for `T`, which is required by `Struct<T>: PartialEq`
    |
 note: required for `Struct<T>` to implement `PartialEq`
-  --> $DIR/derive-clone-for-eq.rs:7:19
+  --> $DIR/derive-clone-for-eq.rs:8:19
    |
 LL | impl<T: Clone, U> PartialEq<U> for Struct<T>
    |         -----     ^^^^^^^^^^^^     ^^^^^^^^^
diff --git a/tests/ui/suggestions/option-content-move.fixed b/tests/ui/suggestions/option-content-move.fixed
index 4a5a9483c20..ef07d55871e 100644
--- a/tests/ui/suggestions/option-content-move.fixed
+++ b/tests/ui/suggestions/option-content-move.fixed
@@ -1,4 +1,5 @@
 //@ run-rustfix
+#[allow(dead_code)]
 pub struct LipogramCorpora {
     selections: Vec<(char, Option<String>)>,
 }
@@ -17,6 +18,7 @@ impl LipogramCorpora {
     }
 }
 
+#[allow(dead_code)]
 pub struct LipogramCorpora2 {
     selections: Vec<(char, Result<String, String>)>,
 }
diff --git a/tests/ui/suggestions/option-content-move.rs b/tests/ui/suggestions/option-content-move.rs
index 90d05c74399..5be6358fd6a 100644
--- a/tests/ui/suggestions/option-content-move.rs
+++ b/tests/ui/suggestions/option-content-move.rs
@@ -1,4 +1,5 @@
 //@ run-rustfix
+#[allow(dead_code)]
 pub struct LipogramCorpora {
     selections: Vec<(char, Option<String>)>,
 }
@@ -17,6 +18,7 @@ impl LipogramCorpora {
     }
 }
 
+#[allow(dead_code)]
 pub struct LipogramCorpora2 {
     selections: Vec<(char, Result<String, String>)>,
 }
diff --git a/tests/ui/suggestions/option-content-move.stderr b/tests/ui/suggestions/option-content-move.stderr
index a382a04344a..b4ec5b180d2 100644
--- a/tests/ui/suggestions/option-content-move.stderr
+++ b/tests/ui/suggestions/option-content-move.stderr
@@ -1,5 +1,5 @@
 error[E0507]: cannot move out of `selection.1` which is behind a shared reference
-  --> $DIR/option-content-move.rs:10:20
+  --> $DIR/option-content-move.rs:11:20
    |
 LL |                 if selection.1.unwrap().contains(selection.0) {
    |                    ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
@@ -19,7 +19,7 @@ LL |                 if selection.1.clone().unwrap().contains(selection.0) {
    |                               ++++++++
 
 error[E0507]: cannot move out of `selection.1` which is behind a shared reference
-  --> $DIR/option-content-move.rs:28:20
+  --> $DIR/option-content-move.rs:30:20
    |
 LL |                 if selection.1.unwrap().contains(selection.0) {
    |                    ^^^^^^^^^^^ -------- `selection.1` moved due to this method call
diff --git a/tests/ui/traits/object/generics.rs b/tests/ui/traits/object/generics.rs
index 462b0bc5bb7..0ae562c0d30 100644
--- a/tests/ui/traits/object/generics.rs
+++ b/tests/ui/traits/object/generics.rs
@@ -7,6 +7,7 @@ pub trait Trait2<A> {
     fn doit(&self) -> A;
 }
 
+#[allow(dead_code)]
 pub struct Impl<A1, A2, A3> {
     m1: marker::PhantomData<(A1,A2,A3)>,
     /*