about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs26
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs2
-rw-r--r--compiler/rustc_lint/src/builtin.rs23
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs11
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs2
-rw-r--r--compiler/rustc_middle/src/query/mod.rs4
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs4
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs61
-rw-r--r--library/core/src/intrinsics/mir.rs2
-rw-r--r--library/core/src/iter/traits/iterator.rs8
-rw-r--r--src/librustdoc/html/format.rs2
-rw-r--r--src/librustdoc/html/markdown.rs14
-rw-r--r--src/librustdoc/html/render/context.rs2
-rw-r--r--src/librustdoc/html/render/print_item.rs91
-rw-r--r--src/librustdoc/html/sources.rs2
-rw-r--r--src/librustdoc/html/static/js/search.js96
-rw-r--r--src/librustdoc/html/templates/short_item_info.html2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs2
-rw-r--r--src/librustdoc/visit_ast.rs8
-rw-r--r--src/tools/tidy/src/mir_opt_tests.rs41
-rw-r--r--tests/rustdoc-js/search-bag-semantics.js20
-rw-r--r--tests/rustdoc-js/search-bag-semantics.rs4
-rw-r--r--tests/rustdoc-json/fns/extern_c_variadic.rs9
-rw-r--r--tests/rustdoc/deprecated.rs4
-rw-r--r--tests/rustdoc/footnote-in-summary.rs17
-rw-r--r--tests/rustdoc/issue-109258-missing-private-inlining.rs27
-rw-r--r--tests/ui/lint/clashing-extern-fn.rs33
-rw-r--r--tests/ui/lint/clashing-extern-fn.stderr18
-rw-r--r--tests/ui/test-attrs/custom-test-frameworks/issue-107454.rs10
-rw-r--r--tests/ui/test-attrs/custom-test-frameworks/issue-107454.stderr15
31 files changed, 400 insertions, 162 deletions
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 007d14b0e74..151afd2d458 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -33,7 +33,23 @@ pub fn expand_test_case(
     }
 
     let sp = ecx.with_def_site_ctxt(attr_sp);
-    let mut item = anno_item.expect_item();
+    let (mut item, is_stmt) = match anno_item {
+        Annotatable::Item(item) => (item, false),
+        Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
+            (i, true)
+        } else {
+            unreachable!()
+        },
+        _ => {
+            ecx.struct_span_err(
+                anno_item.span(),
+                "`#[test_case]` attribute is only allowed on items",
+            )
+            .emit();
+
+            return vec![];
+        }
+    };
     item = item.map(|mut item| {
         let test_path_symbol = Symbol::intern(&item_path(
             // skip the name of the root module
@@ -50,7 +66,13 @@ pub fn expand_test_case(
         item
     });
 
-    return vec![Annotatable::Item(item)];
+    let ret = if is_stmt {
+        Annotatable::Stmt(P(ecx.stmt_item(item.span, item)))
+    } else {
+        Annotatable::Item(item)
+    };
+
+    vec![ret]
 }
 
 pub fn expand_test(
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 2e788de9fab..71b54e11cc0 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -3068,7 +3068,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                         // generate the def_id of an associated type for the trait and return as
                         // type a projection.
                         let def_id = if in_trait && tcx.lower_impl_trait_in_trait_to_assoc_ty() {
-                            tcx.associated_item_for_impl_trait_in_trait(local_def_id).to_def_id()
+                            tcx.associated_type_for_impl_trait_in_trait(local_def_id).to_def_id()
                         } else {
                             local_def_id.to_def_id()
                         };
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 5b2100b5da9..b1ff76865ab 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -2781,8 +2781,7 @@ impl ClashingExternDeclarations {
 
             // Given a transparent newtype, reach through and grab the inner
             // type unless the newtype makes the type non-null.
-            let non_transparent_ty = |ty: Ty<'tcx>| -> Ty<'tcx> {
-                let mut ty = ty;
+            let non_transparent_ty = |mut ty: Ty<'tcx>| -> Ty<'tcx> {
                 loop {
                     if let ty::Adt(def, substs) = *ty.kind() {
                         let is_transparent = def.repr().transparent();
@@ -2792,14 +2791,14 @@ impl ClashingExternDeclarations {
                             ty, is_transparent, is_non_null
                         );
                         if is_transparent && !is_non_null {
-                            debug_assert!(def.variants().len() == 1);
+                            debug_assert_eq!(def.variants().len(), 1);
                             let v = &def.variant(VariantIdx::new(0));
-                            ty = transparent_newtype_field(tcx, v)
-                                .expect(
-                                    "single-variant transparent structure with zero-sized field",
-                                )
-                                .ty(tcx, substs);
-                            continue;
+                            // continue with `ty`'s non-ZST field,
+                            // otherwise `ty` is a ZST and we can return
+                            if let Some(field) = transparent_newtype_field(tcx, v) {
+                                ty = field.ty(tcx, substs);
+                                continue;
+                            }
                         }
                     }
                     debug!("non_transparent_ty -> {:?}", ty);
@@ -2813,10 +2812,8 @@ impl ClashingExternDeclarations {
             if !seen_types.insert((a, b)) {
                 // We've encountered a cycle. There's no point going any further -- the types are
                 // structurally the same.
-                return true;
-            }
-            let tcx = cx.tcx;
-            if a == b {
+                true
+            } else if a == b {
                 // All nominally-same types are structurally same, too.
                 true
             } else {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 98eae70b4b7..561e770d90c 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -254,7 +254,7 @@ provide! { tcx, def_id, other, cdata,
             .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys")))
      }
 
-    associated_items_for_impl_trait_in_trait => { table_defaulted_array }
+    associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array }
 
     visibility => { cdata.get_visibility(def_id.index) }
     adt_def => { cdata.get_adt_def(def_id.index, tcx) }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 19745988c59..46fd0cace09 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -609,10 +609,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
         _ = stat!("mir", || self.encode_mir());
 
-        _ = stat!("items", || {
-            self.encode_def_ids();
-            self.encode_info_for_items();
-        });
+        _ = stat!("def-ids", || self.encode_def_ids());
+
+        _ = stat!("items", || self.encode_info_for_items());
 
         let interpret_alloc_index = stat!("interpret-alloc-index", || {
             let mut interpret_alloc_index = Vec::new();
@@ -1198,8 +1197,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 record!(self.tables.trait_impl_trait_tys[def_id] <- table);
             }
             if should_encode_fn_impl_trait_in_trait(tcx, def_id) {
-                let table = tcx.associated_items_for_impl_trait_in_trait(def_id);
-                record_defaulted_array!(self.tables.associated_items_for_impl_trait_in_trait[def_id] <- table);
+                let table = tcx.associated_types_for_impl_traits_in_associated_fn(def_id);
+                record_defaulted_array!(self.tables.associated_types_for_impl_traits_in_associated_fn[def_id] <- table);
             }
         }
 
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 6f20ac9e527..6dc6041b284 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -354,7 +354,7 @@ define_tables! {
     explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Predicate<'static>, Span)>>,
     inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
     inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
-    associated_items_for_impl_trait_in_trait: Table<DefIndex, LazyArray<DefId>>,
+    associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>,
     opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>,
     unused_generic_params: Table<DefIndex, UnusedGenericParams>,
 
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index c8860cc55f6..4bf81c97d06 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -785,7 +785,7 @@ rustc_queries! {
     /// if `fn_def_id` is the def id of a function defined inside an impl that implements a trait, then it
     /// creates and returns the associated items that correspond to each impl trait in return position
     /// of the implemented trait.
-    query associated_items_for_impl_trait_in_trait(fn_def_id: DefId) -> &'tcx [DefId] {
+    query associated_types_for_impl_traits_in_associated_fn(fn_def_id: DefId) -> &'tcx [DefId] {
         desc { |tcx| "creating associated items for impl trait in trait returned by `{}`", tcx.def_path_str(fn_def_id) }
         cache_on_disk_if { fn_def_id.is_local() }
         separate_provide_extern
@@ -793,7 +793,7 @@ rustc_queries! {
 
     /// Given an impl trait in trait `opaque_ty_def_id`, create and return the corresponding
     /// associated item.
-    query associated_item_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId {
+    query associated_type_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId {
         desc { |tcx| "creates the associated item corresponding to the opaque type `{}`", tcx.def_path_str(opaque_ty_def_id.to_def_id()) }
         cache_on_disk_if { true }
         separate_provide_extern
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 6ef8384d010..298b2c3073c 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -2579,7 +2579,9 @@ impl<'tcx> TyCtxt<'tcx> {
         let Some(trait_item_def_id) = item.trait_item_def_id else { return false; };
 
         if self.lower_impl_trait_in_trait_to_assoc_ty() {
-            return !self.associated_items_for_impl_trait_in_trait(trait_item_def_id).is_empty();
+            return !self
+                .associated_types_for_impl_traits_in_associated_fn(trait_item_def_id)
+                .is_empty();
         }
 
         // FIXME(RPITIT): This does a somewhat manual walk through the signature
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index 68b1086e8e3..b5bd6a81ff8 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -11,8 +11,8 @@ pub fn provide(providers: &mut ty::query::Providers) {
         associated_item,
         associated_item_def_ids,
         associated_items,
-        associated_items_for_impl_trait_in_trait,
-        associated_item_for_impl_trait_in_trait,
+        associated_types_for_impl_traits_in_associated_fn,
+        associated_type_for_impl_trait_in_trait,
         impl_item_implementor_ids,
         ..*providers
     };
@@ -24,7 +24,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
         hir::ItemKind::Trait(.., ref trait_item_refs) => {
             if tcx.lower_impl_trait_in_trait_to_assoc_ty() {
                 // We collect RPITITs for each trait method's return type and create a
-                // corresponding associated item using associated_items_for_impl_trait_in_trait
+                // corresponding associated item using associated_types_for_impl_traits_in_associated_fn
                 // query.
                 tcx.arena.alloc_from_iter(
                     trait_item_refs
@@ -39,7 +39,9 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
                                 .flat_map(|trait_item_ref| {
                                     let trait_fn_def_id =
                                         trait_item_ref.id.owner_id.def_id.to_def_id();
-                                    tcx.associated_items_for_impl_trait_in_trait(trait_fn_def_id)
+                                    tcx.associated_types_for_impl_traits_in_associated_fn(
+                                        trait_fn_def_id,
+                                    )
                                 })
                                 .map(|def_id| *def_id),
                         ),
@@ -56,7 +58,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
             if tcx.lower_impl_trait_in_trait_to_assoc_ty() {
                 // We collect RPITITs for each trait method's return type, on the impl side too and
                 // create a corresponding associated item using
-                // associated_items_for_impl_trait_in_trait query.
+                // associated_types_for_impl_traits_in_associated_fn query.
                 tcx.arena.alloc_from_iter(
                     impl_
                         .items
@@ -72,7 +74,9 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
                                 .flat_map(|impl_item_ref| {
                                     let impl_fn_def_id =
                                         impl_item_ref.id.owner_id.def_id.to_def_id();
-                                    tcx.associated_items_for_impl_trait_in_trait(impl_fn_def_id)
+                                    tcx.associated_types_for_impl_traits_in_associated_fn(
+                                        impl_fn_def_id,
+                                    )
                                 })
                                 .map(|def_id| *def_id)
                         })),
@@ -176,13 +180,19 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A
     }
 }
 
-/// Given an `fn_def_id` of a trait or of an impl that implements a given trait:
-/// if `fn_def_id` is the def id of a function defined inside a trait, then it creates and returns
-/// the associated items that correspond to each impl trait in return position for that trait.
-/// if `fn_def_id` is the def id of a function defined inside an impl that implements a trait, then it
-/// creates and returns the associated items that correspond to each impl trait in return position
-/// of the implemented trait.
-fn associated_items_for_impl_trait_in_trait(tcx: TyCtxt<'_>, fn_def_id: DefId) -> &'_ [DefId] {
+/// Given an `fn_def_id` of a trait or a trait implementation:
+///
+/// if `fn_def_id` is a function defined inside a trait, then it synthesizes
+/// a new def id corresponding to a new associated type for each return-
+/// position `impl Trait` in the signature.
+///
+/// if `fn_def_id` is a function inside of an impl, then for each synthetic
+/// associated type generated for the corresponding trait function described
+/// above, synthesize a corresponding associated type in the impl.
+fn associated_types_for_impl_traits_in_associated_fn(
+    tcx: TyCtxt<'_>,
+    fn_def_id: DefId,
+) -> &'_ [DefId] {
     let parent_def_id = tcx.parent(fn_def_id);
 
     match tcx.def_kind(parent_def_id) {
@@ -206,7 +216,7 @@ fn associated_items_for_impl_trait_in_trait(tcx: TyCtxt<'_>, fn_def_id: DefId) -
                 visitor.visit_fn_ret_ty(output);
 
                 tcx.arena.alloc_from_iter(visitor.rpits.iter().map(|opaque_ty_def_id| {
-                    tcx.associated_item_for_impl_trait_in_trait(opaque_ty_def_id).to_def_id()
+                    tcx.associated_type_for_impl_trait_in_trait(opaque_ty_def_id).to_def_id()
                 }))
             } else {
                 &[]
@@ -217,9 +227,9 @@ fn associated_items_for_impl_trait_in_trait(tcx: TyCtxt<'_>, fn_def_id: DefId) -
             let Some(trait_fn_def_id) = tcx.associated_item(fn_def_id).trait_item_def_id else { return &[] };
 
             tcx.arena.alloc_from_iter(
-                tcx.associated_items_for_impl_trait_in_trait(trait_fn_def_id).iter().map(
+                tcx.associated_types_for_impl_traits_in_associated_fn(trait_fn_def_id).iter().map(
                     move |trait_assoc_def_id| {
-                        impl_associated_item_for_impl_trait_in_trait(
+                        associated_type_for_impl_trait_in_impl(
                             tcx,
                             trait_assoc_def_id.expect_local(),
                             fn_def_id.expect_local(),
@@ -231,16 +241,17 @@ fn associated_items_for_impl_trait_in_trait(tcx: TyCtxt<'_>, fn_def_id: DefId) -
         }
 
         def_kind => bug!(
-            "associated_items_for_impl_trait_in_trait: {:?} should be Trait or Impl but is {:?}",
+            "associated_types_for_impl_traits_in_associated_fn: {:?} should be Trait or Impl but is {:?}",
             parent_def_id,
             def_kind
         ),
     }
 }
 
-/// Given an `opaque_ty_def_id` corresponding to an impl trait in trait, create and return the
-/// corresponding associated item.
-fn associated_item_for_impl_trait_in_trait(
+/// Given an `opaque_ty_def_id` corresponding to an `impl Trait` in an associated
+/// function from a trait, synthesize an associated type for that `impl Trait`
+/// that inherits properties that we infer from the method and the opaque type.
+fn associated_type_for_impl_trait_in_trait(
     tcx: TyCtxt<'_>,
     opaque_ty_def_id: LocalDefId,
 ) -> LocalDefId {
@@ -335,10 +346,12 @@ fn associated_item_for_impl_trait_in_trait(
     local_def_id
 }
 
-/// Given an `trait_assoc_def_id` that corresponds to a previously synthesized impl trait in trait
-/// into an associated type and an `impl_def_id` corresponding to an impl block, create and return
-/// the corresponding associated item inside the impl block.
-fn impl_associated_item_for_impl_trait_in_trait(
+/// Given an `trait_assoc_def_id` corresponding to an associated item synthesized
+/// from an `impl Trait` in an associated function from a trait, and an
+/// `impl_fn_def_id` that represents an implementation of the associated function
+/// that the `impl Trait` comes from, synthesize an associated type for that `impl Trait`
+/// that inherits properties that we infer from the method and the associated type.
+fn associated_type_for_impl_trait_in_impl(
     tcx: TyCtxt<'_>,
     trait_assoc_def_id: LocalDefId,
     impl_fn_def_id: LocalDefId,
diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs
index d2d9771bdce..64fc1c0c277 100644
--- a/library/core/src/intrinsics/mir.rs
+++ b/library/core/src/intrinsics/mir.rs
@@ -8,7 +8,7 @@
 //!
 //! The documentation for this module describes how to use this feature. If you are interested in
 //! hacking on the implementation, most of that documentation lives at
-//! `rustc_mir_building/src/build/custom/mod.rs`.
+//! `rustc_mir_build/src/build/custom/mod.rs`.
 //!
 //! Typical usage will look like this:
 //!
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index 6fc86550b63..6c3030336c6 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -2003,7 +2003,7 @@ pub trait Iterator {
     /// a.iter().map(|&x| x * 2).collect_into(&mut vec);
     /// a.iter().map(|&x| x * 10).collect_into(&mut vec);
     ///
-    /// assert_eq!(vec![0, 1, 2, 4, 6, 10, 20, 30], vec);
+    /// assert_eq!(vec, vec![0, 1, 2, 4, 6, 10, 20, 30]);
     /// ```
     ///
     /// `Vec` can have a manual set capacity to avoid reallocating it:
@@ -2018,7 +2018,7 @@ pub trait Iterator {
     /// a.iter().map(|&x| x * 10).collect_into(&mut vec);
     ///
     /// assert_eq!(6, vec.capacity());
-    /// println!("{:?}", vec);
+    /// assert_eq!(vec, vec![2, 4, 6, 10, 20, 30]);
     /// ```
     ///
     /// The returned mutable reference can be used to continue the call chain:
@@ -2032,12 +2032,12 @@ pub trait Iterator {
     /// let count = a.iter().collect_into(&mut vec).iter().count();
     ///
     /// assert_eq!(count, vec.len());
-    /// println!("Vec len is {}", count);
+    /// assert_eq!(vec, vec![1, 2, 3]);
     ///
     /// let count = a.iter().collect_into(&mut vec).iter().count();
     ///
     /// assert_eq!(count, vec.len());
-    /// println!("Vec len now is {}", count);
+    /// assert_eq!(vec, vec![1, 2, 3, 1, 2, 3]);
     /// ```
     #[inline]
     #[unstable(feature = "iter_collect_into", reason = "new API", issue = "94780")]
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index d526a8be081..f2b9c0bcf3e 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -1480,7 +1480,7 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
                 debug!("path={:?}", path);
                 // modified from `resolved_path()` to work with `DefPathData`
                 let last_name = path.data.last().unwrap().data.get_opt_name().unwrap();
-                let anchor = anchor(vis_did, last_name, cx).to_string();
+                let anchor = anchor(vis_did, last_name, cx);
 
                 let mut s = "pub(in ".to_owned();
                 for seg in &path.data[..path.data.len() - 1] {
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index fe446ae3c16..fd81a21f5a9 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -556,7 +556,15 @@ fn check_if_allowed_tag(t: &Tag<'_>) -> bool {
 }
 
 fn is_forbidden_tag(t: &Tag<'_>) -> bool {
-    matches!(t, Tag::CodeBlock(_) | Tag::Table(_) | Tag::TableHead | Tag::TableRow | Tag::TableCell)
+    matches!(
+        t,
+        Tag::CodeBlock(_)
+            | Tag::Table(_)
+            | Tag::TableHead
+            | Tag::TableRow
+            | Tag::TableCell
+            | Tag::FootnoteDefinition(_)
+    )
 }
 
 impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
@@ -589,6 +597,10 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
                     is_start = false;
                     check_if_allowed_tag(c)
                 }
+                Event::FootnoteReference(_) => {
+                    self.skipped_tags += 1;
+                    false
+                }
                 _ => true,
             };
             if !is_allowed_tag {
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 63cd0e04a28..ac5054ce1b6 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -352,7 +352,7 @@ impl<'tcx> Context<'tcx> {
                 },
             );
 
-            path = href.into_inner().to_string_lossy().to_string();
+            path = href.into_inner().to_string_lossy().into_owned();
 
             if let Some(c) = path.as_bytes().last() && *c != b'/' {
                 path.push('/');
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 577497868f6..7eb9c0b7cf5 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1,5 +1,6 @@
 use clean::AttributesExt;
 
+use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
@@ -28,8 +29,8 @@ use crate::formats::item_type::ItemType;
 use crate::formats::{AssocItemRender, Impl, RenderMode};
 use crate::html::escape::Escape;
 use crate::html::format::{
-    join_with_double_colon, print_abi_with_space, print_constness_with_space, print_where_clause,
-    visibility_print_with_space, Buffer, Ending, PrintWithSpace,
+    display_fn, join_with_double_colon, print_abi_with_space, print_constness_with_space,
+    print_where_clause, visibility_print_with_space, Buffer, Ending, PrintWithSpace,
 };
 use crate::html::layout::Page;
 use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
@@ -367,7 +368,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
                         ..myitem.clone()
                     };
 
-                    let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()));
+                    let stab_tags = Some(extra_info_tags(&import_item, item, cx.tcx()).to_string());
                     stab_tags
                 } else {
                     None
@@ -461,42 +462,62 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
 
 /// Render the stability, deprecation and portability tags that are displayed in the item's summary
 /// at the module level.
-fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> String {
-    let mut tags = String::new();
-
-    fn tag_html(class: &str, title: &str, contents: &str) -> String {
-        format!(r#"<span class="stab {}" title="{}">{}</span>"#, class, Escape(title), contents)
-    }
-
-    // The trailing space after each tag is to space it properly against the rest of the docs.
-    if let Some(depr) = &item.deprecation(tcx) {
-        let message = if stability::deprecation_in_effect(depr) {
-            "Deprecated"
-        } else {
-            "Deprecation planned"
-        };
-        tags += &tag_html("deprecated", "", message);
-    }
+fn extra_info_tags<'a, 'tcx: 'a>(
+    item: &'a clean::Item,
+    parent: &'a clean::Item,
+    tcx: TyCtxt<'tcx>,
+) -> impl fmt::Display + 'a + Captures<'tcx> {
+    display_fn(move |f| {
+        fn tag_html<'a>(
+            class: &'a str,
+            title: &'a str,
+            contents: &'a str,
+        ) -> impl fmt::Display + 'a {
+            display_fn(move |f| {
+                write!(
+                    f,
+                    r#"<span class="stab {}" title="{}">{}</span>"#,
+                    class,
+                    Escape(title),
+                    contents
+                )
+            })
+        }
 
-    // The "rustc_private" crates are permanently unstable so it makes no sense
-    // to render "unstable" everywhere.
-    if item.stability(tcx).as_ref().map(|s| s.is_unstable() && s.feature != sym::rustc_private)
-        == Some(true)
-    {
-        tags += &tag_html("unstable", "", "Experimental");
-    }
+        // The trailing space after each tag is to space it properly against the rest of the docs.
+        if let Some(depr) = &item.deprecation(tcx) {
+            let message = if stability::deprecation_in_effect(depr) {
+                "Deprecated"
+            } else {
+                "Deprecation planned"
+            };
+            write!(f, "{}", tag_html("deprecated", "", message))?;
+        }
 
-    let cfg = match (&item.cfg, parent.cfg.as_ref()) {
-        (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
-        (cfg, _) => cfg.as_deref().cloned(),
-    };
+        // The "rustc_private" crates are permanently unstable so it makes no sense
+        // to render "unstable" everywhere.
+        if item.stability(tcx).as_ref().map(|s| s.is_unstable() && s.feature != sym::rustc_private)
+            == Some(true)
+        {
+            write!(f, "{}", tag_html("unstable", "", "Experimental"))?;
+        }
 
-    debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg);
-    if let Some(ref cfg) = cfg {
-        tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html());
-    }
+        let cfg = match (&item.cfg, parent.cfg.as_ref()) {
+            (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg),
+            (cfg, _) => cfg.as_deref().cloned(),
+        };
 
-    tags
+        debug!("Portability name={:?} {:?} - {:?} = {:?}", item.name, item.cfg, parent.cfg, cfg);
+        if let Some(ref cfg) = cfg {
+            write!(
+                f,
+                "{}",
+                tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html())
+            )
+        } else {
+            Ok(())
+        }
+    })
 }
 
 fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &clean::Function) {
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 2c90bf4fadc..be9d1c408ec 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -85,7 +85,7 @@ impl LocalSourcesCollector<'_, '_> {
             },
         );
 
-        let mut href = href.into_inner().to_string_lossy().to_string();
+        let mut href = href.into_inner().to_string_lossy().into_owned();
         if let Some(c) = href.as_bytes().last() && *c != b'/' {
             href.push('/');
         }
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index c71ce2c3001..36ff20e299e 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -1202,28 +1202,42 @@ function initSearch(rawSearchIndex) {
          * @param {Row} row
          * @param {QueryElement} elem    - The element from the parsed query.
          * @param {integer} typeFilter
+         * @param {Array<integer>} skipPositions - Do not return one of these positions.
          *
-         * @return {integer} - Returns an edit distance to the best match. If there is no
-         *                      match, returns `maxEditDistance + 1`.
+         * @return {dist: integer, position: integer} - Returns an edit distance to the best match.
+         *                                              If there is no match, returns
+         *                                              `maxEditDistance + 1` and position: -1.
          */
-        function findArg(row, elem, typeFilter, maxEditDistance) {
+        function findArg(row, elem, typeFilter, maxEditDistance, skipPositions) {
             let dist = maxEditDistance + 1;
+            let position = -1;
 
             if (row && row.type && row.type.inputs && row.type.inputs.length > 0) {
+                let i = 0;
                 for (const input of row.type.inputs) {
-                    if (!typePassesFilter(typeFilter, input.ty)) {
+                    if (!typePassesFilter(typeFilter, input.ty) ||
+                        skipPositions.indexOf(i) !== -1) {
+                        i += 1;
                         continue;
                     }
-                    dist = Math.min(
-                        dist,
-                        checkType(input, elem, parsedQuery.literalSearch, maxEditDistance)
+                    const typeDist = checkType(
+                        input,
+                        elem,
+                        parsedQuery.literalSearch,
+                        maxEditDistance
                     );
-                    if (dist === 0) {
-                        return 0;
+                    if (typeDist === 0) {
+                        return {dist: 0, position: i};
+                    }
+                    if (typeDist < dist) {
+                        dist = typeDist;
+                        position = i;
                     }
+                    i += 1;
                 }
             }
-            return parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
+            dist = parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
+            return {dist, position};
         }
 
         /**
@@ -1232,29 +1246,43 @@ function initSearch(rawSearchIndex) {
          * @param {Row} row
          * @param {QueryElement} elem   - The element from the parsed query.
          * @param {integer} typeFilter
+         * @param {Array<integer>} skipPositions - Do not return one of these positions.
          *
-         * @return {integer} - Returns an edit distance to the best match. If there is no
-         *                      match, returns `maxEditDistance + 1`.
+         * @return {dist: integer, position: integer} - Returns an edit distance to the best match.
+         *                                              If there is no match, returns
+         *                                              `maxEditDistance + 1` and position: -1.
          */
-        function checkReturned(row, elem, typeFilter, maxEditDistance) {
+        function checkReturned(row, elem, typeFilter, maxEditDistance, skipPositions) {
             let dist = maxEditDistance + 1;
+            let position = -1;
 
             if (row && row.type && row.type.output.length > 0) {
                 const ret = row.type.output;
+                let i = 0;
                 for (const ret_ty of ret) {
-                    if (!typePassesFilter(typeFilter, ret_ty.ty)) {
+                    if (!typePassesFilter(typeFilter, ret_ty.ty) ||
+                        skipPositions.indexOf(i) !== -1) {
+                        i += 1;
                         continue;
                     }
-                    dist = Math.min(
-                        dist,
-                        checkType(ret_ty, elem, parsedQuery.literalSearch, maxEditDistance)
+                    const typeDist = checkType(
+                        ret_ty,
+                        elem,
+                        parsedQuery.literalSearch,
+                        maxEditDistance
                     );
-                    if (dist === 0) {
-                        return 0;
+                    if (typeDist === 0) {
+                        return {dist: 0, position: i};
                     }
+                    if (typeDist < dist) {
+                        dist = typeDist;
+                        position = i;
+                    }
+                    i += 1;
                 }
             }
-            return parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
+            dist = parsedQuery.literalSearch ? maxEditDistance + 1 : dist;
+            return {dist, position};
         }
 
         function checkPath(contains, ty, maxEditDistance) {
@@ -1455,13 +1483,13 @@ function initSearch(rawSearchIndex) {
             const fullId = row.id;
             const searchWord = searchWords[pos];
 
-            const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance);
-            const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxEditDistance);
+            const in_args = findArg(row, elem, parsedQuery.typeFilter, maxEditDistance, []);
+            const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxEditDistance, []);
 
             // path_dist is 0 because no parent path information is currently stored
             // in the search index
-            addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxEditDistance);
-            addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxEditDistance);
+            addIntoResults(results_in_args, fullId, pos, -1, in_args.dist, 0, maxEditDistance);
+            addIntoResults(results_returned, fullId, pos, -1, returned.dist, 0, maxEditDistance);
 
             if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
                 return;
@@ -1534,12 +1562,20 @@ function initSearch(rawSearchIndex) {
 
             // If the result is too "bad", we return false and it ends this search.
             function checkArgs(elems, callback) {
+                const skipPositions = [];
                 for (const elem of elems) {
                     // There is more than one parameter to the query so all checks should be "exact"
-                    const dist = callback(row, elem, NO_TYPE_FILTER, maxEditDistance);
+                    const { dist, position } = callback(
+                        row,
+                        elem,
+                        NO_TYPE_FILTER,
+                        maxEditDistance,
+                        skipPositions
+                    );
                     if (dist <= 1) {
                         nbDist += 1;
                         totalDist += dist;
+                        skipPositions.push(position);
                     } else {
                         return false;
                     }
@@ -1597,9 +1633,17 @@ function initSearch(rawSearchIndex) {
                             row,
                             elem,
                             parsedQuery.typeFilter,
+                            maxEditDistance,
+                            []
+                        );
+                        addIntoResults(
+                            results_others,
+                            row.id,
+                            i,
+                            -1,
+                            in_returned.dist,
                             maxEditDistance
                         );
-                        addIntoResults(results_others, row.id, i, -1, in_returned, maxEditDistance);
                     }
                 }
             } else if (parsedQuery.foundElems > 0) {
diff --git a/src/librustdoc/html/templates/short_item_info.html b/src/librustdoc/html/templates/short_item_info.html
index e3125af0e47..75d155e91c2 100644
--- a/src/librustdoc/html/templates/short_item_info.html
+++ b/src/librustdoc/html/templates/short_item_info.html
@@ -2,7 +2,7 @@
     {% when Self::Deprecation with { message } %}
         <div class="stab deprecated"> {# #}
             <span class="emoji">👎</span> {# #}
-            <span>{{message}}</span> {# #}
+            <span>{{message|safe}}</span> {# #}
         </div> {# #}
     {% when Self::Unstable with { feature, tracking } %}
         <div class="stab unstable"> {# #}
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 358f6ad566c..6ed7b989999 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -286,7 +286,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
             split.next().map(|f|  Symbol::intern(f)).ok_or_else(no_res)?;
         let path = split
             .next()
-            .map(|f| f.to_owned())
             // If there's no third component, we saw `[a::b]` before and it failed to resolve.
             // So there's no partial res.
             .ok_or_else(no_res)?;
@@ -429,7 +428,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
         let item_name = Symbol::intern(item_str);
         let path_root = split
             .next()
-            .map(|f| f.to_owned())
             // If there's no `::`, it's not an associated item.
             // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
             .ok_or_else(|| {
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index e09a68069e8..060062db002 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -265,10 +265,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             return false;
         }
 
-        if !self.view_item_stack.insert(res_did) {
-            return false;
-        }
-
         if !please_inline &&
             let mut visitor = OneLevelVisitor::new(self.cx.tcx.hir(), res_did) &&
             let Some(item) = visitor.find_target(self.cx.tcx, def_id.to_def_id(), path) &&
@@ -285,6 +281,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             return false;
         }
 
+        if !self.view_item_stack.insert(res_did) {
+            return false;
+        }
+
         let ret = match tcx.hir().get_by_def_id(res_did) {
             Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
                 let prev = mem::replace(&mut self.inlining, true);
diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs
index 2a9dcac2e8d..b316e9e9009 100644
--- a/src/tools/tidy/src/mir_opt_tests.rs
+++ b/src/tools/tidy/src/mir_opt_tests.rs
@@ -3,19 +3,24 @@
 use std::collections::HashSet;
 use std::path::{Path, PathBuf};
 
+use crate::walk::walk_no_read;
+
 fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) {
     let mut rs_files = Vec::<PathBuf>::new();
     let mut output_files = HashSet::<PathBuf>::new();
-    let files = walkdir::WalkDir::new(&path.join("mir-opt")).into_iter();
 
-    for file in files.filter_map(Result::ok).filter(|e| e.file_type().is_file()) {
-        let filepath = file.path();
-        if filepath.extension() == Some("rs".as_ref()) {
-            rs_files.push(filepath.to_owned());
-        } else {
-            output_files.insert(filepath.to_owned());
-        }
-    }
+    walk_no_read(
+        &[&path.join("mir-opt")],
+        |path| path.file_name() == Some("README.md".as_ref()),
+        &mut |file| {
+            let filepath = file.path();
+            if filepath.extension() == Some("rs".as_ref()) {
+                rs_files.push(filepath.to_owned());
+            } else {
+                output_files.insert(filepath.to_owned());
+            }
+        },
+    );
 
     for file in rs_files {
         for bw in [32, 64] {
@@ -26,16 +31,14 @@ fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) {
     }
 
     for extra in output_files {
-        if extra.file_name() != Some("README.md".as_ref()) {
-            if !bless {
-                tidy_error!(
-                    bad,
-                    "the following output file is not associated with any mir-opt test, you can remove it: {}",
-                    extra.display()
-                );
-            } else {
-                let _ = std::fs::remove_file(extra);
-            }
+        if !bless {
+            tidy_error!(
+                bad,
+                "the following output file is not associated with any mir-opt test, you can remove it: {}",
+                extra.display()
+            );
+        } else {
+            let _ = std::fs::remove_file(extra);
         }
     }
 }
diff --git a/tests/rustdoc-js/search-bag-semantics.js b/tests/rustdoc-js/search-bag-semantics.js
new file mode 100644
index 00000000000..c56a3df5f90
--- /dev/null
+++ b/tests/rustdoc-js/search-bag-semantics.js
@@ -0,0 +1,20 @@
+// exact-check
+
+const QUERY = [
+    'P',
+    'P, P',
+];
+
+const EXPECTED = [
+    {
+        'in_args': [
+            { 'path': 'search_bag_semantics', 'name': 'alacazam' },
+            { 'path': 'search_bag_semantics', 'name': 'abracadabra' },
+        ],
+    },
+    {
+        'others': [
+            { 'path': 'search_bag_semantics', 'name': 'abracadabra' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/search-bag-semantics.rs b/tests/rustdoc-js/search-bag-semantics.rs
new file mode 100644
index 00000000000..546572dc4ef
--- /dev/null
+++ b/tests/rustdoc-js/search-bag-semantics.rs
@@ -0,0 +1,4 @@
+pub struct P;
+
+pub fn abracadabra(a: P, b: P) {}
+pub fn alacazam(a: P) {}
diff --git a/tests/rustdoc-json/fns/extern_c_variadic.rs b/tests/rustdoc-json/fns/extern_c_variadic.rs
new file mode 100644
index 00000000000..33bebbab5e0
--- /dev/null
+++ b/tests/rustdoc-json/fns/extern_c_variadic.rs
@@ -0,0 +1,9 @@
+#![feature(no_core)]
+#![no_core]
+
+extern "C" {
+    // @is "$.index[*][?(@.name == 'not_variadic')].inner.decl.c_variadic" false
+    pub fn not_variadic(_: i32);
+    // @is "$.index[*][?(@.name == 'variadic')].inner.decl.c_variadic" true
+    pub fn variadic(_: i32, ...);
+}
diff --git a/tests/rustdoc/deprecated.rs b/tests/rustdoc/deprecated.rs
index 51860441b35..9c9c0945b8f 100644
--- a/tests/rustdoc/deprecated.rs
+++ b/tests/rustdoc/deprecated.rs
@@ -28,6 +28,6 @@ pub struct V;
 pub struct W;
 
 // @matches deprecated/struct.X.html '//*[@class="stab deprecated"]' \
-//      'Deprecated: shorthand reason$'
-#[deprecated = "shorthand reason"]
+//      'Deprecated: shorthand reason: code$'
+#[deprecated = "shorthand reason: `code`"]
 pub struct X;
diff --git a/tests/rustdoc/footnote-in-summary.rs b/tests/rustdoc/footnote-in-summary.rs
new file mode 100644
index 00000000000..e6ff5a7fd51
--- /dev/null
+++ b/tests/rustdoc/footnote-in-summary.rs
@@ -0,0 +1,17 @@
+// This test ensures that no footnote reference is generated inside
+// summary doc.
+
+#![crate_name = "foo"]
+
+// @has 'foo/index.html'
+// @has - '//*[@class="desc docblock-short"]' 'hello bla'
+// @!has - '//*[@class="desc docblock-short"]/sup' '1'
+
+// @has 'foo/struct.S.html'
+// @has - '//*[@class="docblock"]//sup' '1'
+// @has - '//*[@class="docblock"]' 'hello 1 bla'
+
+/// hello [^foot] bla
+///
+/// [^foot]: blabla
+pub struct S;
diff --git a/tests/rustdoc/issue-109258-missing-private-inlining.rs b/tests/rustdoc/issue-109258-missing-private-inlining.rs
new file mode 100644
index 00000000000..96f606368b2
--- /dev/null
+++ b/tests/rustdoc/issue-109258-missing-private-inlining.rs
@@ -0,0 +1,27 @@
+// Regression test for <https://github.com/rust-lang/rust/issues/109258>.
+
+#![crate_name = "foo"]
+
+// @has 'foo/index.html'
+// We should only have a "Re-exports" and a "Modules" headers.
+// @count - '//*[@id="main-content"]/h2[@class="small-section-header"]' 2
+// @has - '//*[@id="main-content"]/h2[@class="small-section-header"]' 'Re-exports'
+// @has - '//*[@id="main-content"]/h2[@class="small-section-header"]' 'Modules'
+
+// @has - '//*[@id="reexport.Foo"]' 'pub use crate::issue_109258::Foo;'
+// @has - '//*[@id="reexport.Foo"]//a[@href="issue_109258/struct.Foo.html"]' 'Foo'
+// @!has 'foo/struct.Foo.html'
+pub use crate::issue_109258::Foo;
+
+// @has 'foo/issue_109258/index.html'
+// We should only have a "Structs" header.
+// @count - '//*[@id="main-content"]/h2[@class="small-section-header"]' 1
+// @has - '//*[@id="main-content"]/h2[@class="small-section-header"]' 'Structs'
+// @has - '//*[@id="main-content"]//a[@href="struct.Foo.html"]' 'Foo'
+// @has 'foo/issue_109258/struct.Foo.html'
+pub mod issue_109258 {
+    mod priv_mod {
+        pub struct Foo;
+    }
+    pub use self::priv_mod::Foo;
+}
diff --git a/tests/ui/lint/clashing-extern-fn.rs b/tests/ui/lint/clashing-extern-fn.rs
index 809e0602671..09fda33dbec 100644
--- a/tests/ui/lint/clashing-extern-fn.rs
+++ b/tests/ui/lint/clashing-extern-fn.rs
@@ -122,8 +122,8 @@ mod banana {
             weight: u32,
             length: u16,
         } // note: distinct type
-          // This should not trigger the lint because two::Banana is structurally equivalent to
-          // one::Banana.
+        // This should not trigger the lint because two::Banana is structurally equivalent to
+        // one::Banana.
         extern "C" {
             fn weigh_banana(count: *const Banana) -> u64;
         }
@@ -223,6 +223,27 @@ mod transparent {
     }
 }
 
+#[allow(improper_ctypes)]
+mod zst {
+    mod transparent {
+        #[repr(transparent)]
+        struct TransparentZst(());
+        extern "C" {
+            fn zst() -> ();
+            fn transparent_zst() -> TransparentZst;
+        }
+    }
+
+    mod not_transparent {
+        struct NotTransparentZst(());
+        extern "C" {
+            // These shouldn't warn since all return types are zero sized
+            fn zst() -> NotTransparentZst;
+            fn transparent_zst() -> NotTransparentZst;
+        }
+    }
+}
+
 mod missing_return_type {
     mod a {
         extern "C" {
@@ -397,10 +418,14 @@ mod hidden_niche {
         use std::num::NonZeroUsize;
 
         #[repr(transparent)]
-        struct Transparent { x: NonZeroUsize }
+        struct Transparent {
+            x: NonZeroUsize,
+        }
 
         #[repr(transparent)]
-        struct TransparentNoNiche { y: UnsafeCell<NonZeroUsize> }
+        struct TransparentNoNiche {
+            y: UnsafeCell<NonZeroUsize>,
+        }
 
         extern "C" {
             fn hidden_niche_transparent() -> Option<Transparent>;
diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr
index 217eed6c92c..5d457ba0ec7 100644
--- a/tests/ui/lint/clashing-extern-fn.stderr
+++ b/tests/ui/lint/clashing-extern-fn.stderr
@@ -130,7 +130,7 @@ LL |             fn transparent_incorrect() -> isize;
               found `unsafe extern "C" fn() -> isize`
 
 warning: `missing_return_type` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:238:13
+  --> $DIR/clashing-extern-fn.rs:259:13
    |
 LL |             fn missing_return_type() -> usize;
    |             ---------------------------------- `missing_return_type` previously declared here
@@ -142,7 +142,7 @@ LL |             fn missing_return_type();
               found `unsafe extern "C" fn()`
 
 warning: `non_zero_usize` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:256:13
+  --> $DIR/clashing-extern-fn.rs:277:13
    |
 LL |             fn non_zero_usize() -> core::num::NonZeroUsize;
    |             ----------------------------------------------- `non_zero_usize` previously declared here
@@ -154,7 +154,7 @@ LL |             fn non_zero_usize() -> usize;
               found `unsafe extern "C" fn() -> usize`
 
 warning: `non_null_ptr` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:258:13
+  --> $DIR/clashing-extern-fn.rs:279:13
    |
 LL |             fn non_null_ptr() -> core::ptr::NonNull<usize>;
    |             ----------------------------------------------- `non_null_ptr` previously declared here
@@ -166,7 +166,7 @@ LL |             fn non_null_ptr() -> *const usize;
               found `unsafe extern "C" fn() -> *const usize`
 
 warning: `option_non_zero_usize_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:356:13
+  --> $DIR/clashing-extern-fn.rs:377:13
    |
 LL |             fn option_non_zero_usize_incorrect() -> usize;
    |             ---------------------------------------------- `option_non_zero_usize_incorrect` previously declared here
@@ -178,7 +178,7 @@ LL |             fn option_non_zero_usize_incorrect() -> isize;
               found `unsafe extern "C" fn() -> isize`
 
 warning: `option_non_null_ptr_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:358:13
+  --> $DIR/clashing-extern-fn.rs:379:13
    |
 LL |             fn option_non_null_ptr_incorrect() -> *const usize;
    |             --------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here
@@ -190,7 +190,7 @@ LL |             fn option_non_null_ptr_incorrect() -> *const isize;
               found `unsafe extern "C" fn() -> *const isize`
 
 warning: `hidden_niche_transparent_no_niche` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:408:13
+  --> $DIR/clashing-extern-fn.rs:433:13
    |
 LL |             fn hidden_niche_transparent_no_niche() -> usize;
    |             ------------------------------------------------ `hidden_niche_transparent_no_niche` previously declared here
@@ -202,7 +202,7 @@ LL |             fn hidden_niche_transparent_no_niche() -> Option<TransparentNoN
               found `unsafe extern "C" fn() -> Option<TransparentNoNiche>`
 
 warning: `hidden_niche_unsafe_cell` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:412:13
+  --> $DIR/clashing-extern-fn.rs:437:13
    |
 LL |             fn hidden_niche_unsafe_cell() -> usize;
    |             --------------------------------------- `hidden_niche_unsafe_cell` previously declared here
@@ -214,7 +214,7 @@ LL |             fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZeroUsize
               found `unsafe extern "C" fn() -> Option<UnsafeCell<NonZeroUsize>>`
 
 warning: `extern` block uses type `Option<TransparentNoNiche>`, which is not FFI-safe
-  --> $DIR/clashing-extern-fn.rs:408:55
+  --> $DIR/clashing-extern-fn.rs:433:55
    |
 LL |             fn hidden_niche_transparent_no_niche() -> Option<TransparentNoNiche>;
    |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -224,7 +224,7 @@ LL |             fn hidden_niche_transparent_no_niche() -> Option<TransparentNoN
    = note: `#[warn(improper_ctypes)]` on by default
 
 warning: `extern` block uses type `Option<UnsafeCell<NonZeroUsize>>`, which is not FFI-safe
-  --> $DIR/clashing-extern-fn.rs:412:46
+  --> $DIR/clashing-extern-fn.rs:437:46
    |
 LL |             fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZeroUsize>>;
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
diff --git a/tests/ui/test-attrs/custom-test-frameworks/issue-107454.rs b/tests/ui/test-attrs/custom-test-frameworks/issue-107454.rs
new file mode 100644
index 00000000000..2bb133e8bfd
--- /dev/null
+++ b/tests/ui/test-attrs/custom-test-frameworks/issue-107454.rs
@@ -0,0 +1,10 @@
+// compile-flags: --test
+
+#![feature(custom_test_frameworks)]
+#![deny(unnameable_test_items)]
+
+fn foo() {
+    #[test_case]
+    //~^ ERROR cannot test inner items [unnameable_test_items]
+    fn test2() {}
+}
diff --git a/tests/ui/test-attrs/custom-test-frameworks/issue-107454.stderr b/tests/ui/test-attrs/custom-test-frameworks/issue-107454.stderr
new file mode 100644
index 00000000000..bd604afb79f
--- /dev/null
+++ b/tests/ui/test-attrs/custom-test-frameworks/issue-107454.stderr
@@ -0,0 +1,15 @@
+error: cannot test inner items
+  --> $DIR/issue-107454.rs:7:5
+   |
+LL |     #[test_case]
+   |     ^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-107454.rs:4:9
+   |
+LL | #![deny(unnameable_test_items)]
+   |         ^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in the attribute macro `test_case` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+