about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/ast.rs21
-rw-r--r--compiler/rustc_ast/src/visit.rs9
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs86
-rw-r--r--compiler/rustc_ast_passes/messages.ftl5
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs51
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs26
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs8
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs56
-rw-r--r--compiler/rustc_attr_parsing/messages.ftl1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs6
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/cfg.rs5
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs6
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/confusables.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/deprecation.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/inline.rs8
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/link_attrs.rs15
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs13
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/must_use.rs5
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/path.rs5
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs8
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/repr.rs8
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs4
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/stability.rs58
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/test_attrs.rs11
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/traits.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/transparency.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs5
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_accessible.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs32
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs22
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl3
-rw-r--r--compiler/rustc_codegen_ssa/src/back/apple.rs24
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs83
-rw-r--r--compiler/rustc_const_eval/src/interpret/call.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs8
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/util.rs14
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs2
-rw-r--r--compiler/rustc_expand/src/base.rs5
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs457
-rw-r--r--compiler/rustc_hir/src/attrs/data_structures.rs3
-rw-r--r--compiler/rustc_hir/src/attrs/encode_cross_crate.rs1
-rw-r--r--compiler/rustc_hir/src/hir.rs28
-rw-r--r--compiler/rustc_hir/src/intravisit.rs26
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs93
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs36
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs17
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs5
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs61
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs10
-rw-r--r--compiler/rustc_lint/messages.ftl1
-rw-r--r--compiler/rustc_lint/src/builtin.rs25
-rw-r--r--compiler/rustc_lint/src/deref_into_dyn_supertrait.rs4
-rw-r--r--compiler/rustc_lint/src/early/diagnostics.rs4
-rw-r--r--compiler/rustc_lint/src/internal.rs12
-rw-r--r--compiler/rustc_lint/src/lints.rs3
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs4
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs2
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs1
-rw-r--r--compiler/rustc_parse/messages.ftl5
-rw-r--r--compiler/rustc_parse/src/errors.rs14
-rw-r--r--compiler/rustc_parse/src/parser/item.rs54
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs41
-rw-r--r--compiler/rustc_passes/messages.ftl6
-rw-r--r--compiler/rustc_passes/src/check_attr.rs59
-rw-r--r--compiler/rustc_passes/src/dead.rs6
-rw-r--r--compiler/rustc_passes/src/errors.rs4
-rw-r--r--compiler/rustc_passes/src/stability.rs24
-rw-r--r--compiler/rustc_privacy/src/lib.rs3
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs7
-rw-r--r--compiler/rustc_resolve/src/late.rs14
-rw-r--r--compiler/rustc_target/src/spec/base/apple/mod.rs24
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs8
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs4
-rw-r--r--compiler/rustc_ty_utils/src/implied_bounds.rs8
-rw-r--r--compiler/rustc_ty_utils/src/sig_types.rs2
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs6
-rw-r--r--library/alloc/src/vec/mod.rs72
-rw-r--r--library/std/src/sys/pal/uefi/tests.rs57
-rw-r--r--library/std/src/sys/pal/uefi/time.rs159
-rw-r--r--src/ci/docker/host-x86_64/pr-check-2/Dockerfile6
m---------src/doc/reference0
m---------src/doc/rust-by-example0
-rw-r--r--src/doc/rustc-dev-guide/src/stability.md7
-rw-r--r--src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md2
-rw-r--r--src/librustdoc/clean/mod.rs9
-rw-r--r--src/librustdoc/html/render/print_item.rs8
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/copy_iterator.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_drop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/error_impl_error.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/infallible_try_from.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_trait_methods.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/op_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/same_name_method.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/serde_api.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unconditional_recursion.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils/mod.rs26
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs3
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.fixed24
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.rs18
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.stderr134
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-96721.stderr2
-rw-r--r--src/tools/miri/src/borrow_tracker/mod.rs24
-rw-r--r--src/tools/rustfmt/src/items.rs51
-rw-r--r--src/tools/rustfmt/tests/source/negative-impl.rs4
-rw-r--r--src/tools/rustfmt/tests/target/negative-impl.rs8
-rw-r--r--tests/run-make/link-under-xcode/foo.rs1
-rw-r--r--tests/run-make/link-under-xcode/rmake.rs32
-rw-r--r--tests/rustdoc-ui/intra-doc/github-flavored-admonitions.rs6
-rw-r--r--tests/rustdoc/enum/enum-variant-value.rs22
-rw-r--r--tests/ui/attributes/crate-name-macro-call.stderr2
-rw-r--r--tests/ui/attributes/crate-type-delimited.stderr19
-rw-r--r--tests/ui/attributes/crate-type-empty.stderr15
-rw-r--r--tests/ui/attributes/crate-type-macro-call.stderr19
-rw-r--r--tests/ui/attributes/invalid-macro-use.rs4
-rw-r--r--tests/ui/attributes/invalid-macro-use.stderr26
-rw-r--r--tests/ui/attributes/invalid-reprs.stderr1
-rw-r--r--tests/ui/attributes/lint_on_root.rs2
-rw-r--r--tests/ui/attributes/lint_on_root.stderr4
-rw-r--r--tests/ui/attributes/malformed-attrs.rs2
-rw-r--r--tests/ui/attributes/malformed-attrs.stderr210
-rw-r--r--tests/ui/attributes/malformed-reprs.stderr22
-rw-r--r--tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr15
-rw-r--r--tests/ui/attributes/used_with_multi_args.stderr5
-rw-r--r--tests/ui/cfg/cfg-target-compact-errors.stderr8
-rw-r--r--tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs5
-rw-r--r--tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr28
-rw-r--r--tests/ui/deprecation/deprecation-sanity.stderr48
-rw-r--r--tests/ui/error-codes/E0197.stderr2
-rw-r--r--tests/ui/error-codes/E0540.stderr7
-rw-r--r--tests/ui/error-codes/E0565-1.stderr8
-rw-r--r--tests/ui/extern/issue-47725.rs1
-rw-r--r--tests/ui/extern/issue-47725.stderr2
-rw-r--r--tests/ui/feature-gates/feature-gate-optimize_attribute.stderr17
-rw-r--r--tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr4
-rw-r--r--tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr2
-rw-r--r--tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr2
-rw-r--r--tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr2
-rw-r--r--tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr2
-rw-r--r--tests/ui/invalid/invalid-inline.stderr13
-rw-r--r--tests/ui/issues/issue-43988.stderr50
-rw-r--r--tests/ui/link-native-libs/link-attr-validation-early.rs4
-rw-r--r--tests/ui/link-native-libs/link-attr-validation-early.stderr12
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr4
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs2
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr6
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs2
-rw-r--r--tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr6
-rw-r--r--tests/ui/lint/dead-code/self-assign.rs17
-rw-r--r--tests/ui/lint/dead-code/self-assign.stderr44
-rw-r--r--tests/ui/lint/lint-malformed.stderr15
-rw-r--r--tests/ui/macros/macro-use-bad-args-1.stderr1
-rw-r--r--tests/ui/macros/macro-use-bad-args-2.stderr1
-rw-r--r--tests/ui/malformed/malformed-regressions.rs4
-rw-r--r--tests/ui/malformed/malformed-regressions.stderr22
-rw-r--r--tests/ui/modules/path-invalid-form.stderr2
-rw-r--r--tests/ui/modules/path-macro.stderr2
-rw-r--r--tests/ui/parser/bad-lit-suffixes.stderr1
-rw-r--r--tests/ui/parser/default-on-wrong-item-kind.rs8
-rw-r--r--tests/ui/parser/default-on-wrong-item-kind.stderr42
-rw-r--r--tests/ui/proc-macro/attribute.rs16
-rw-r--r--tests/ui/proc-macro/attribute.stderr268
-rw-r--r--tests/ui/recursion_limit/invalid_digit_type.stderr2
-rw-r--r--tests/ui/recursion_limit/invalid_macro.stderr2
-rw-r--r--tests/ui/recursion_limit/no-value.stderr2
-rw-r--r--tests/ui/repr/invalid_repr_list_help.stderr5
-rw-r--r--tests/ui/repr/repr.stderr62
-rw-r--r--tests/ui/resolve/path-attr-in-const-block.stderr2
-rw-r--r--tests/ui/span/E0539.stderr6
-rw-r--r--tests/ui/specialization/defaultimpl/validation.rs2
-rw-r--r--tests/ui/specialization/defaultimpl/validation.stderr4
-rw-r--r--tests/ui/test-attrs/test-should-panic-attr.rs4
-rw-r--r--tests/ui/test-attrs/test-should-panic-attr.stderr10
-rw-r--r--tests/ui/traits/const-traits/inherent-impl.rs4
-rw-r--r--tests/ui/traits/const-traits/inherent-impl.stderr8
-rw-r--r--tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr26
-rw-r--r--tests/ui/traits/const-traits/span-bug-issue-121418.rs2
-rw-r--r--tests/ui/traits/const-traits/span-bug-issue-121418.stderr4
-rw-r--r--tests/ui/traits/safety-inherent-impl.stderr2
-rw-r--r--tests/ui/traits/syntax-trait-polarity.stderr20
-rw-r--r--tests/ui/unpretty/exhaustive.hir.stdout2
-rw-r--r--tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.fail.stderr18
-rw-r--r--tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.rs33
-rw-r--r--tests/ui/unstable-feature-bound/unstable_inherent_method.rs4
-rw-r--r--tests/ui/unstable-feature-bound/unstable_inherent_method.stderr8
-rw-r--r--triagebot.toml1
219 files changed, 2677 insertions, 1179 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 8160ed3cc46..87c9c797ea5 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -3661,15 +3661,19 @@ pub struct TyAlias {
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct Impl {
+    pub generics: Generics,
+    pub of_trait: Option<Box<TraitImplHeader>>,
+    pub self_ty: Box<Ty>,
+    pub items: ThinVec<Box<AssocItem>>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct TraitImplHeader {
     pub defaultness: Defaultness,
     pub safety: Safety,
-    pub generics: Generics,
     pub constness: Const,
     pub polarity: ImplPolarity,
-    /// The trait being implemented, if any.
-    pub of_trait: Option<TraitRef>,
-    pub self_ty: Box<Ty>,
-    pub items: ThinVec<Box<AssocItem>>,
+    pub trait_ref: TraitRef,
 }
 
 #[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
@@ -3793,7 +3797,7 @@ pub enum ItemKind {
     /// An implementation.
     ///
     /// E.g., `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`.
-    Impl(Box<Impl>),
+    Impl(Impl),
     /// A macro invocation.
     ///
     /// E.g., `foo!(..)`.
@@ -3880,7 +3884,7 @@ impl ItemKind {
             | Self::Union(_, generics, _)
             | Self::Trait(box Trait { generics, .. })
             | Self::TraitAlias(_, generics, _)
-            | Self::Impl(box Impl { generics, .. }) => Some(generics),
+            | Self::Impl(Impl { generics, .. }) => Some(generics),
             _ => None,
         }
     }
@@ -4040,7 +4044,7 @@ mod size_asserts {
     static_assert_size!(GenericArg, 24);
     static_assert_size!(GenericBound, 88);
     static_assert_size!(Generics, 40);
-    static_assert_size!(Impl, 136);
+    static_assert_size!(Impl, 64);
     static_assert_size!(Item, 144);
     static_assert_size!(ItemKind, 80);
     static_assert_size!(LitKind, 24);
@@ -4053,6 +4057,7 @@ mod size_asserts {
     static_assert_size!(PathSegment, 24);
     static_assert_size!(Stmt, 32);
     static_assert_size!(StmtKind, 16);
+    static_assert_size!(TraitImplHeader, 80);
     static_assert_size!(Ty, 64);
     static_assert_size!(TyKind, 40);
     // tidy-alphabetical-end
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 5fdce27db53..68b3d2b0368 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -929,8 +929,13 @@ macro_rules! common_visitor_and_walkers {
         }
 
         impl_walkable!(|&$($mut)? $($lt)? self: Impl, vis: &mut V| {
-            let Impl { defaultness, safety, generics, constness, polarity, of_trait, self_ty, items } = self;
-            visit_visitable!($($mut)? vis, defaultness, safety, generics, constness, polarity, of_trait, self_ty);
+            let Impl { generics, of_trait, self_ty, items } = self;
+            try_visit!(vis.visit_generics(generics));
+            if let Some(box of_trait) = of_trait {
+                let TraitImplHeader { defaultness, safety, constness, polarity, trait_ref } = of_trait;
+                visit_visitable!($($mut)? vis, defaultness, safety, constness, polarity, trait_ref);
+            }
+            try_visit!(vis.visit_ty(self_ty));
             visit_visitable_with!($($mut)? vis, items, AssocCtxt::Impl { of_trait: of_trait.is_some() });
             V::Result::output()
         });
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index ac6fac4c08e..235573c96e4 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -340,13 +340,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 );
                 hir::ItemKind::Union(ident, generics, vdata)
             }
-            ItemKind::Impl(box Impl {
-                safety,
-                polarity,
-                defaultness,
-                constness,
+            ItemKind::Impl(Impl {
                 generics: ast_generics,
-                of_trait: trait_ref,
+                of_trait,
                 self_ty: ty,
                 items: impl_items,
             }) => {
@@ -364,54 +360,30 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // lifetime to be added, but rather a reference to a
                 // parent lifetime.
                 let itctx = ImplTraitContext::Universal;
-                let (generics, (trait_ref, lowered_ty)) =
+                let (generics, (of_trait, lowered_ty)) =
                     self.lower_generics(ast_generics, id, itctx, |this| {
-                        let modifiers = TraitBoundModifiers {
-                            constness: BoundConstness::Never,
-                            asyncness: BoundAsyncness::Normal,
-                            // we don't use this in bound lowering
-                            polarity: BoundPolarity::Positive,
-                        };
-
-                        let trait_ref = trait_ref.as_ref().map(|trait_ref| {
-                            this.lower_trait_ref(
-                                modifiers,
-                                trait_ref,
-                                ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
-                            )
-                        });
+                        let of_trait = of_trait
+                            .as_deref()
+                            .map(|of_trait| this.lower_trait_impl_header(of_trait));
 
                         let lowered_ty = this.lower_ty(
                             ty,
                             ImplTraitContext::Disallowed(ImplTraitPosition::ImplSelf),
                         );
 
-                        (trait_ref, lowered_ty)
+                        (of_trait, lowered_ty)
                     });
 
                 let new_impl_items = self
                     .arena
                     .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
 
-                // `defaultness.has_value()` is never called for an `impl`, always `true` in order
-                // to not cause an assertion failure inside the `lower_defaultness` function.
-                let has_val = true;
-                let (defaultness, defaultness_span) = self.lower_defaultness(*defaultness, has_val);
-                let polarity = match polarity {
-                    ImplPolarity::Positive => ImplPolarity::Positive,
-                    ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)),
-                };
-                hir::ItemKind::Impl(self.arena.alloc(hir::Impl {
-                    constness: self.lower_constness(*constness),
-                    safety: self.lower_safety(*safety, hir::Safety::Safe),
-                    polarity,
-                    defaultness,
-                    defaultness_span,
+                hir::ItemKind::Impl(hir::Impl {
                     generics,
-                    of_trait: trait_ref,
+                    of_trait,
                     self_ty: lowered_ty,
                     items: new_impl_items,
-                }))
+                })
             }
             ItemKind::Trait(box Trait {
                 constness,
@@ -982,6 +954,44 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.expr(span, hir::ExprKind::Err(guar))
     }
 
+    fn lower_trait_impl_header(
+        &mut self,
+        trait_impl_header: &TraitImplHeader,
+    ) -> &'hir hir::TraitImplHeader<'hir> {
+        let TraitImplHeader { constness, safety, polarity, defaultness, ref trait_ref } =
+            *trait_impl_header;
+        let constness = self.lower_constness(constness);
+        let safety = self.lower_safety(safety, hir::Safety::Safe);
+        let polarity = match polarity {
+            ImplPolarity::Positive => ImplPolarity::Positive,
+            ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(s)),
+        };
+        // `defaultness.has_value()` is never called for an `impl`, always `true` in order
+        // to not cause an assertion failure inside the `lower_defaultness` function.
+        let has_val = true;
+        let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val);
+        let modifiers = TraitBoundModifiers {
+            constness: BoundConstness::Never,
+            asyncness: BoundAsyncness::Normal,
+            // we don't use this in bound lowering
+            polarity: BoundPolarity::Positive,
+        };
+        let trait_ref = self.lower_trait_ref(
+            modifiers,
+            trait_ref,
+            ImplTraitContext::Disallowed(ImplTraitPosition::Trait),
+        );
+
+        self.arena.alloc(hir::TraitImplHeader {
+            constness,
+            safety,
+            polarity,
+            defaultness,
+            defaultness_span,
+            trait_ref,
+        })
+    }
+
     fn lower_impl_item(
         &mut self,
         i: &AssocItem,
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 53e64439afc..340a1a239c5 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -175,11 +175,6 @@ ast_passes_generic_default_trailing = generic parameters with a default must be
 ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed
     .help = remove one of these features
 
-ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
-    .because = {$annotation} because of this
-    .type = inherent impl for this type
-    .only_trait = only trait implementations may be annotated with {$annotation}
-
 ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot be declared with `safe` safety qualifier
     .suggestion = remove safe from this item
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 9d3b0969ef3..dc2eb17589c 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -954,13 +954,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         }
 
         match &item.kind {
-            ItemKind::Impl(box Impl {
-                safety,
-                polarity,
-                defaultness: _,
-                constness,
+            ItemKind::Impl(Impl {
                 generics,
-                of_trait: Some(t),
+                of_trait:
+                    Some(box TraitImplHeader {
+                        safety,
+                        polarity,
+                        defaultness: _,
+                        constness,
+                        trait_ref: t,
+                    }),
                 self_ty,
                 items,
             }) => {
@@ -992,46 +995,12 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     walk_list!(this, visit_assoc_item, items, AssocCtxt::Impl { of_trait: true });
                 });
             }
-            ItemKind::Impl(box Impl {
-                safety,
-                polarity,
-                defaultness,
-                constness,
-                generics,
-                of_trait: None,
-                self_ty,
-                items,
-            }) => {
-                let error = |annotation_span, annotation, only_trait| errors::InherentImplCannot {
-                    span: self_ty.span,
-                    annotation_span,
-                    annotation,
-                    self_ty: self_ty.span,
-                    only_trait,
-                };
-
+            ItemKind::Impl(Impl { generics, of_trait: None, self_ty, items }) => {
                 self.visit_attrs_vis(&item.attrs, &item.vis);
                 self.visibility_not_permitted(
                     &item.vis,
                     errors::VisibilityNotPermittedNote::IndividualImplItems,
                 );
-                if let &Safety::Unsafe(span) = safety {
-                    self.dcx().emit_err(errors::InherentImplCannotUnsafe {
-                        span: self_ty.span,
-                        annotation_span: span,
-                        annotation: "unsafe",
-                        self_ty: self_ty.span,
-                    });
-                }
-                if let &ImplPolarity::Negative(span) = polarity {
-                    self.dcx().emit_err(error(span, "negative", false));
-                }
-                if let &Defaultness::Default(def_span) = defaultness {
-                    self.dcx().emit_err(error(def_span, "`default`", true));
-                }
-                if let &Const::Yes(span) = constness {
-                    self.dcx().emit_err(error(span, "`const`", true));
-                }
 
                 self.with_tilde_const(Some(TildeConstReason::Impl { span: item.span }), |this| {
                     this.visit_generics(generics)
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 60f47490f12..1cb2493afe8 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -465,32 +465,6 @@ pub(crate) struct UnsafeNegativeImpl {
 }
 
 #[derive(Diagnostic)]
-#[diag(ast_passes_inherent_cannot_be)]
-pub(crate) struct InherentImplCannot<'a> {
-    #[primary_span]
-    pub span: Span,
-    #[label(ast_passes_because)]
-    pub annotation_span: Span,
-    pub annotation: &'a str,
-    #[label(ast_passes_type)]
-    pub self_ty: Span,
-    #[note(ast_passes_only_trait)]
-    pub only_trait: bool,
-}
-
-#[derive(Diagnostic)]
-#[diag(ast_passes_inherent_cannot_be, code = E0197)]
-pub(crate) struct InherentImplCannotUnsafe<'a> {
-    #[primary_span]
-    pub span: Span,
-    #[label(ast_passes_because)]
-    pub annotation_span: Span,
-    pub annotation: &'a str,
-    #[label(ast_passes_type)]
-    pub self_ty: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(ast_passes_unsafe_item)]
 pub(crate) struct UnsafeItem {
     #[primary_span]
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 662357ce884..c9344a76a7b 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -217,18 +217,18 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 }
             }
 
-            ast::ItemKind::Impl(box ast::Impl { polarity, defaultness, of_trait, .. }) => {
-                if let &ast::ImplPolarity::Negative(span) = polarity {
+            ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) => {
+                if let ast::ImplPolarity::Negative(span) = of_trait.polarity {
                     gate!(
                         &self,
                         negative_impls,
-                        span.to(of_trait.as_ref().map_or(span, |t| t.path.span)),
+                        span.to(of_trait.trait_ref.path.span),
                         "negative trait bounds are not fully implemented; \
                          use marker types for now"
                     );
                 }
 
-                if let ast::Defaultness::Default(_) = defaultness {
+                if let ast::Defaultness::Default(_) = of_trait.defaultness {
                     gate!(&self, specialization, i.span, "specialization is unstable");
                 }
             }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 6e34d1b61db..ab402cbb8dc 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -308,39 +308,41 @@ impl<'a> State<'a> {
                 let (cb, ib) = self.head(visibility_qualified(&item.vis, "union"));
                 self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib);
             }
-            ast::ItemKind::Impl(box ast::Impl {
-                safety,
-                polarity,
-                defaultness,
-                constness,
-                generics,
-                of_trait,
-                self_ty,
-                items,
-            }) => {
+            ast::ItemKind::Impl(ast::Impl { generics, of_trait, self_ty, items }) => {
                 let (cb, ib) = self.head("");
                 self.print_visibility(&item.vis);
-                self.print_defaultness(*defaultness);
-                self.print_safety(*safety);
-                self.word("impl");
-
-                if generics.params.is_empty() {
-                    self.nbsp();
-                } else {
-                    self.print_generic_params(&generics.params);
-                    self.space();
-                }
 
-                self.print_constness(*constness);
+                let impl_generics = |this: &mut Self| {
+                    this.word("impl");
 
-                if let ast::ImplPolarity::Negative(_) = polarity {
-                    self.word("!");
-                }
-
-                if let Some(t) = of_trait {
-                    self.print_trait_ref(t);
+                    if generics.params.is_empty() {
+                        this.nbsp();
+                    } else {
+                        this.print_generic_params(&generics.params);
+                        this.space();
+                    }
+                };
+
+                if let Some(box of_trait) = of_trait {
+                    let ast::TraitImplHeader {
+                        defaultness,
+                        safety,
+                        constness,
+                        polarity,
+                        ref trait_ref,
+                    } = *of_trait;
+                    self.print_defaultness(defaultness);
+                    self.print_safety(safety);
+                    impl_generics(self);
+                    self.print_constness(constness);
+                    if let ast::ImplPolarity::Negative(_) = polarity {
+                        self.word("!");
+                    }
+                    self.print_trait_ref(trait_ref);
                     self.space();
                     self.word_space("for");
+                } else {
+                    impl_generics(self);
                 }
 
                 self.print_type(self_ty);
diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl
index 35ff48cb5f2..de22ea322c7 100644
--- a/compiler/rustc_attr_parsing/messages.ftl
+++ b/compiler/rustc_attr_parsing/messages.ftl
@@ -132,6 +132,7 @@ attr_parsing_unknown_version_literal =
 attr_parsing_unrecognized_repr_hint =
     unrecognized representation hint
     .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+    .note = for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
 
 attr_parsing_unstable_cfg_target_compact =
     compact `cfg(target(..))` is experimental and subject to change
diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
index 95104b896ac..b3393e93de8 100644
--- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
@@ -15,7 +15,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
     type Item = (Symbol, Span);
     const CONVERT: ConvertFn<Self::Item> =
         |items, span| AttributeKind::AllowInternalUnstable(items, span);
-    const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
+    const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
 
     fn extend<'c>(
         cx: &'c mut AcceptContext<'_, '_, S>,
@@ -32,7 +32,7 @@ impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
     const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound];
     type Item = (Symbol, Span);
     const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items);
-    const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
+    const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
 
     fn extend<'c>(
         cx: &'c mut AcceptContext<'_, '_, S>,
@@ -53,7 +53,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
     type Item = Symbol;
     const CONVERT: ConvertFn<Self::Item> =
         |items, first_span| AttributeKind::AllowConstFnUnstable(items, first_span);
-    const TEMPLATE: AttributeTemplate = template!(Word, List: "feat1, feat2, ...");
+    const TEMPLATE: AttributeTemplate = template!(Word, List: &["feat1, feat2, ..."]);
 
     fn extend<'c>(
         cx: &'c mut AcceptContext<'_, '_, S>,
diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
index 947be28bc95..695ee666476 100644
--- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs
@@ -16,7 +16,10 @@ use crate::{
     CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg,
 };
 
-pub const CFG_TEMPLATE: AttributeTemplate = template!(List: "predicate");
+pub const CFG_TEMPLATE: AttributeTemplate = template!(
+    List: &["predicate"],
+    "https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute"
+);
 
 pub fn parse_cfg_attr<'c, S: Stage>(
     cx: &'c mut AcceptContext<'_, '_, S>,
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index c5fb11dbf6a..a9f77195d1b 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -17,7 +17,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
     const PATH: &[Symbol] = &[sym::optimize];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
-    const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
+    const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let Some(list) = args.list() else {
@@ -253,7 +253,7 @@ pub(crate) struct UsedParser {
 impl<S: Stage> AttributeParser<S> for UsedParser {
     const ATTRIBUTES: AcceptMapping<Self, S> = &[(
         &[sym::used],
-        template!(Word, List: "compiler|linker"),
+        template!(Word, List: &["compiler", "linker"]),
         |group: &mut Self, cx, args| {
             let used_by = match args {
                 ArgParser::NoArgs => UsedBy::Linker,
@@ -327,7 +327,7 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
     type Item = (Symbol, Span);
     const PATH: &[Symbol] = &[sym::target_feature];
     const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
-    const TEMPLATE: AttributeTemplate = template!(List: "enable = \"feat1, feat2\"");
+    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
 
     fn extend<'c>(
         cx: &'c mut AcceptContext<'_, '_, S>,
diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
index 7d24c89a6e8..edd22172ca2 100644
--- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
@@ -16,7 +16,7 @@ pub(crate) struct ConfusablesParser {
 impl<S: Stage> AttributeParser<S> for ConfusablesParser {
     const ATTRIBUTES: AcceptMapping<Self, S> = &[(
         &[sym::rustc_confusables],
-        template!(List: r#""name1", "name2", ..."#),
+        template!(List: &[r#""name1", "name2", ..."#]),
         |this, cx, args| {
             let Some(list) = args.list() else {
                 cx.expected_list(cx.attr_span);
diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
index 38ec4bd5645..e57ea8bbb5c 100644
--- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
@@ -40,7 +40,7 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
     const TEMPLATE: AttributeTemplate = template!(
         Word,
-        List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
+        List: &[r#"since = "version""#, r#"note = "reason""#, r#"since = "version", note = "reason""#],
         NameValueStr: "reason"
     );
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs
index 8437713206e..e9a45f20bff 100644
--- a/compiler/rustc_attr_parsing/src/attributes/inline.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs
@@ -18,7 +18,11 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
     const PATH: &'static [Symbol] = &[sym::inline];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
-    const TEMPLATE: AttributeTemplate = template!(Word, List: "always|never");
+    const TEMPLATE: AttributeTemplate = template!(
+        Word,
+        List: &["always", "never"],
+        "https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute"
+    );
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         match args {
@@ -59,7 +63,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
     const PATH: &'static [Symbol] = &[sym::rustc_force_inline];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
-    const TEMPLATE: AttributeTemplate = template!(Word, List: "reason", NameValueStr: "reason");
+    const TEMPLATE: AttributeTemplate = template!(Word, List: &["reason"], NameValueStr: "reason");
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let reason = match args {
diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
index 7eab3090870..d406c30b83e 100644
--- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
@@ -16,7 +16,10 @@ impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
     const PATH: &[Symbol] = &[sym::link_name];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
-    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
+    const TEMPLATE: AttributeTemplate = template!(
+        NameValueStr: "name",
+        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"
+    );
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let Some(nv) = args.name_value() else {
@@ -38,7 +41,10 @@ impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
     const PATH: &[Symbol] = &[sym::link_section];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
-    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
+    const TEMPLATE: AttributeTemplate = template!(
+        NameValueStr: "name",
+        "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
+    );
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let Some(nv) = args.name_value() else {
@@ -94,7 +100,10 @@ impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
     const PATH: &[Symbol] = &[sym::link_ordinal];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
-    const TEMPLATE: AttributeTemplate = template!(List: "ordinal");
+    const TEMPLATE: AttributeTemplate = template!(
+        List: &["ordinal"],
+        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
+    );
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let ordinal = parse_single_integer(cx, args)?;
diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
index 886f7a889d3..a1166bf9ac5 100644
--- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
@@ -31,7 +31,10 @@ pub(crate) struct MacroUseParser {
     first_span: Option<Span>,
 }
 
-const MACRO_USE_TEMPLATE: AttributeTemplate = template!(Word, List: "name1, name2, ...");
+const MACRO_USE_TEMPLATE: AttributeTemplate = template!(
+    Word, List: &["name1, name2, ..."],
+    "https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute"
+);
 
 impl<S: Stage> AttributeParser<S> for MacroUseParser {
     const ATTRIBUTES: AcceptMapping<Self, S> = &[(
@@ -113,3 +116,11 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
         Some(AttributeKind::MacroUse { span: self.first_span?, arguments: self.state })
     }
 }
+
+pub(crate) struct AllowInternalUnsafeParser;
+
+impl<S: Stage> NoArgsAttributeParser<S> for AllowInternalUnsafeParser {
+    const PATH: &[Symbol] = &[sym::allow_internal_unsafe];
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
+    const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span);
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
index d767abbc250..c88bb5a69e5 100644
--- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
@@ -14,7 +14,10 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
     const PATH: &[Symbol] = &[sym::must_use];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
-    const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason");
+    const TEMPLATE: AttributeTemplate = template!(
+        Word, NameValueStr: "reason",
+        "https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"
+    );
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         Some(AttributeKind::MustUse {
diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs
index 5700d780d71..c1c3de8cbfc 100644
--- a/compiler/rustc_attr_parsing/src/attributes/path.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/path.rs
@@ -12,7 +12,10 @@ impl<S: Stage> SingleAttributeParser<S> for PathParser {
     const PATH: &[Symbol] = &[sym::path];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
-    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "file");
+    const TEMPLATE: AttributeTemplate = template!(
+        NameValueStr: "file",
+        "https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute"
+    );
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let Some(nv) = args.name_value() else {
diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
index b156a7c5845..b267980914c 100644
--- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
@@ -28,8 +28,10 @@ impl<S: Stage> SingleAttributeParser<S> for ProcMacroDeriveParser {
     const PATH: &[Symbol] = &[sym::proc_macro_derive];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
-    const TEMPLATE: AttributeTemplate =
-        template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
+    const TEMPLATE: AttributeTemplate = template!(
+        List: &["TraitName", "TraitName, attributes(name1, name2, ...)"],
+        "https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros"
+    );
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?;
@@ -47,7 +49,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcBuiltinMacroParser {
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
     const TEMPLATE: AttributeTemplate =
-        template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)");
+        template!(List: &["TraitName", "TraitName, attributes(name1, name2, ...)"]);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?;
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index 6087afe6ded..996d2af5f37 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -26,8 +26,10 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
     const CONVERT: ConvertFn<Self::Item> =
         |items, first_span| AttributeKind::Repr { reprs: items, first_span };
     // FIXME(jdonszelmann): never used
-    const TEMPLATE: AttributeTemplate =
-        template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");
+    const TEMPLATE: AttributeTemplate = template!(
+        List: &["C", "Rust", "transparent", "align(...)", "packed(...)", "<integer type>"],
+        "https://doc.rust-lang.org/reference/type-layout.html#representations"
+    );
 
     fn extend<'c>(
         cx: &'c mut AcceptContext<'_, '_, S>,
@@ -275,7 +277,7 @@ pub(crate) struct AlignParser(Option<(Align, Span)>);
 
 impl AlignParser {
     const PATH: &'static [Symbol] = &[sym::rustc_align];
-    const TEMPLATE: AttributeTemplate = template!(List: "<alignment in bytes>");
+    const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
 
     fn parse<'c, S: Stage>(
         &mut self,
diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
index b465d2e62ff..1a668b4416f 100644
--- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
@@ -12,7 +12,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {
     const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_start];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
-    const TEMPLATE: AttributeTemplate = template!(List: "start");
+    const TEMPLATE: AttributeTemplate = template!(List: &["start"]);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         parse_single_integer(cx, args)
@@ -26,7 +26,7 @@ impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeEnd {
     const PATH: &'static [Symbol] = &[sym::rustc_layout_scalar_valid_range_end];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
-    const TEMPLATE: AttributeTemplate = template!(List: "end");
+    const TEMPLATE: AttributeTemplate = template!(List: &["end"]);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         parse_single_integer(cx, args)
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index 3c4ec133d51..c6707f5048b 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -48,7 +48,7 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
     const ATTRIBUTES: AcceptMapping<Self, S> = &[
         (
             &[sym::stable],
-            template!(List: r#"feature = "name", since = "version""#),
+            template!(List: &[r#"feature = "name", since = "version""#]),
             |this, cx, args| {
                 reject_outside_std!(cx);
                 if !this.check_duplicate(cx)
@@ -60,7 +60,7 @@ impl<S: Stage> AttributeParser<S> for StabilityParser {
         ),
         (
             &[sym::unstable],
-            template!(List: r#"feature = "name", reason = "...", issue = "N""#),
+            template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]),
             |this, cx, args| {
                 reject_outside_std!(cx);
                 if !this.check_duplicate(cx)
@@ -131,7 +131,7 @@ pub(crate) struct BodyStabilityParser {
 impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
     const ATTRIBUTES: AcceptMapping<Self, S> = &[(
         &[sym::rustc_default_body_unstable],
-        template!(List: r#"feature = "name", reason = "...", issue = "N""#),
+        template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]),
         |this, cx, args| {
             reject_outside_std!(cx);
             if this.stability.is_some() {
@@ -177,29 +177,37 @@ impl ConstStabilityParser {
 
 impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
     const ATTRIBUTES: AcceptMapping<Self, S> = &[
-        (&[sym::rustc_const_stable], template!(List: r#"feature = "name""#), |this, cx, args| {
-            reject_outside_std!(cx);
+        (
+            &[sym::rustc_const_stable],
+            template!(List: &[r#"feature = "name""#]),
+            |this, cx, args| {
+                reject_outside_std!(cx);
 
-            if !this.check_duplicate(cx)
-                && let Some((feature, level)) = parse_stability(cx, args)
-            {
-                this.stability = Some((
-                    PartialConstStability { level, feature, promotable: false },
-                    cx.attr_span,
-                ));
-            }
-        }),
-        (&[sym::rustc_const_unstable], template!(List: r#"feature = "name""#), |this, cx, args| {
-            reject_outside_std!(cx);
-            if !this.check_duplicate(cx)
-                && let Some((feature, level)) = parse_unstability(cx, args)
-            {
-                this.stability = Some((
-                    PartialConstStability { level, feature, promotable: false },
-                    cx.attr_span,
-                ));
-            }
-        }),
+                if !this.check_duplicate(cx)
+                    && let Some((feature, level)) = parse_stability(cx, args)
+                {
+                    this.stability = Some((
+                        PartialConstStability { level, feature, promotable: false },
+                        cx.attr_span,
+                    ));
+                }
+            },
+        ),
+        (
+            &[sym::rustc_const_unstable],
+            template!(List: &[r#"feature = "name""#]),
+            |this, cx, args| {
+                reject_outside_std!(cx);
+                if !this.check_duplicate(cx)
+                    && let Some((feature, level)) = parse_unstability(cx, args)
+                {
+                    this.stability = Some((
+                        PartialConstStability { level, feature, promotable: false },
+                        cx.attr_span,
+                    ));
+                }
+            },
+        ),
         (&[sym::rustc_promotable], template!(Word), |this, cx, _| {
             reject_outside_std!(cx);
             this.promotable = true;
diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
index 77b494328c7..3267855fb0d 100644
--- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
@@ -13,7 +13,10 @@ impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
     const PATH: &[Symbol] = &[sym::ignore];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
-    const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason");
+    const TEMPLATE: AttributeTemplate = template!(
+        Word, NameValueStr: "reason",
+        "https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute"
+    );
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         Some(AttributeKind::Ignore {
@@ -51,8 +54,10 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
     const PATH: &[Symbol] = &[sym::should_panic];
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
-    const TEMPLATE: AttributeTemplate =
-        template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason");
+    const TEMPLATE: AttributeTemplate = template!(
+        Word, List: &[r#"expected = "reason""#], NameValueStr: "reason",
+        "https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute"
+    );
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         Some(AttributeKind::ShouldPanic {
diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs
index a954617ca57..8514d799aa4 100644
--- a/compiler/rustc_attr_parsing/src/attributes/traits.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs
@@ -16,7 +16,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
 
-    const TEMPLATE: AttributeTemplate = template!(List: "array, boxed_slice");
+    const TEMPLATE: AttributeTemplate = template!(List: &["array, boxed_slice"]);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let mut array = false;
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
index 1c57dc1ebe2..d4d68eb8b27 100644
--- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -19,7 +19,7 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
         cx.dcx().span_err(vec![used, unused], "multiple macro transparency attributes");
     });
     const TEMPLATE: AttributeTemplate =
-        template!(NameValueStr: "transparent|semitransparent|opaque");
+        template!(NameValueStr: ["transparent", "semitransparent", "opaque"]);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let Some(nv) = args.name_value() else {
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 80dfdffdb55..1420753a44e 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -33,7 +33,9 @@ use crate::attributes::lint_helpers::{
     AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
 };
 use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
-use crate::attributes::macro_attrs::{MacroEscapeParser, MacroUseParser};
+use crate::attributes::macro_attrs::{
+    AllowInternalUnsafeParser, MacroEscapeParser, MacroUseParser,
+};
 use crate::attributes::must_use::MustUseParser;
 use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
 use crate::attributes::non_exhaustive::NonExhaustiveParser;
@@ -178,6 +180,7 @@ attribute_parsers!(
         Single<SkipDuringMethodDispatchParser>,
         Single<TransparencyParser>,
         Single<WithoutArgs<AllowIncoherentImplParser>>,
+        Single<WithoutArgs<AllowInternalUnsafeParser>>,
         Single<WithoutArgs<AsPtrParser>>,
         Single<WithoutArgs<AutomaticallyDerivedParser>>,
         Single<WithoutArgs<CoherenceIsCoreParser>>,
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 1de25ca252b..41179844152 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -498,6 +498,7 @@ pub(crate) struct ReprIdent {
 #[derive(Diagnostic)]
 #[diag(attr_parsing_unrecognized_repr_hint, code = E0552)]
 #[help]
+#[note]
 pub(crate) struct UnrecognizedReprHint {
     #[primary_span]
     pub span: Span,
@@ -690,6 +691,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
             }
         }
 
+        if let Some(link) = self.template.docs {
+            diag.note(format!("for more information, visit <{link}>"));
+        }
         let suggestions = self.template.suggestions(false, &name);
         diag.span_suggestions(
             self.attr_span,
diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
index 5f203dd5d11..f7d8f4aa783 100644
--- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
@@ -44,7 +44,7 @@ impl MultiItemModifier for Expander {
         item: Annotatable,
         _is_derive_const: bool,
     ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
-        let template = AttributeTemplate { list: Some("path"), ..Default::default() };
+        let template = AttributeTemplate { list: Some(&["path"]), ..Default::default() };
         validate_attr::check_builtin_meta_item(
             &ecx.sess.psess,
             meta_item,
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index e259f5b3955..a33eca43de5 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -34,8 +34,10 @@ impl MultiItemModifier for Expander {
         let (sess, features) = (ecx.sess, ecx.ecfg.features);
         let result =
             ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
-                let template =
-                    AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
+                let template = AttributeTemplate {
+                    list: Some(&["Trait1, Trait2, ..."]),
+                    ..Default::default()
+                };
                 validate_attr::check_builtin_meta_item(
                     &sess.psess,
                     meta_item,
diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
index 6082e376435..75db5d77783 100644
--- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
@@ -108,11 +108,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
             cx.item(
                 span,
                 attrs.clone(),
-                ast::ItemKind::Impl(Box::new(ast::Impl {
-                    safety: ast::Safety::Default,
-                    polarity: ast::ImplPolarity::Positive,
-                    defaultness: ast::Defaultness::Final,
-                    constness: ast::Const::No,
+                ast::ItemKind::Impl(ast::Impl {
                     generics: Generics {
                         params: generics
                             .params
@@ -137,10 +133,16 @@ pub(crate) fn expand_deriving_coerce_pointee(
                         where_clause: generics.where_clause.clone(),
                         span: generics.span,
                     },
-                    of_trait: Some(trait_ref),
+                    of_trait: Some(Box::new(ast::TraitImplHeader {
+                        safety: ast::Safety::Default,
+                        polarity: ast::ImplPolarity::Positive,
+                        defaultness: ast::Defaultness::Final,
+                        constness: ast::Const::No,
+                        trait_ref,
+                    })),
                     self_ty: self_type.clone(),
                     items: ThinVec::new(),
-                })),
+                }),
             ),
         ));
     }
@@ -152,16 +154,18 @@ pub(crate) fn expand_deriving_coerce_pointee(
         let item = cx.item(
             span,
             attrs.clone(),
-            ast::ItemKind::Impl(Box::new(ast::Impl {
-                safety: ast::Safety::Default,
-                polarity: ast::ImplPolarity::Positive,
-                defaultness: ast::Defaultness::Final,
-                constness: ast::Const::No,
+            ast::ItemKind::Impl(ast::Impl {
                 generics,
-                of_trait: Some(trait_ref),
+                of_trait: Some(Box::new(ast::TraitImplHeader {
+                    safety: ast::Safety::Default,
+                    polarity: ast::ImplPolarity::Positive,
+                    defaultness: ast::Defaultness::Final,
+                    constness: ast::Const::No,
+                    trait_ref,
+                })),
                 self_ty: self_type.clone(),
                 items: ThinVec::new(),
-            })),
+            }),
         );
         push(Annotatable::Item(item));
     };
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 3f4b47152c4..3fcf9da9450 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -826,21 +826,25 @@ impl<'a> TraitDef<'a> {
             )
         }
 
-        let opt_trait_ref = Some(trait_ref);
-
         cx.item(
             self.span,
             attrs,
-            ast::ItemKind::Impl(Box::new(ast::Impl {
-                safety: ast::Safety::Default,
-                polarity: ast::ImplPolarity::Positive,
-                defaultness: ast::Defaultness::Final,
-                constness: if self.is_const { ast::Const::Yes(DUMMY_SP) } else { ast::Const::No },
+            ast::ItemKind::Impl(ast::Impl {
                 generics: trait_generics,
-                of_trait: opt_trait_ref,
+                of_trait: Some(Box::new(ast::TraitImplHeader {
+                    safety: ast::Safety::Default,
+                    polarity: ast::ImplPolarity::Positive,
+                    defaultness: ast::Defaultness::Final,
+                    constness: if self.is_const {
+                        ast::Const::Yes(DUMMY_SP)
+                    } else {
+                        ast::Const::No
+                    },
+                    trait_ref,
+                })),
                 self_ty: self_type,
                 items: methods.into_iter().chain(associated_types).collect(),
-            })),
+            }),
         )
     }
 
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 3ca070acc9d..b6cfea88363 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -401,6 +401,9 @@ codegen_ssa_version_script_write_failure = failed to write version script: {$err
 
 codegen_ssa_visual_studio_not_installed = you may need to install Visual Studio build tools with the "C++ build tools" workload
 
+codegen_ssa_xcrun_about =
+    the SDK is needed by the linker to know where to find symbols in system libraries and for embedding the SDK version in the final object file
+
 codegen_ssa_xcrun_command_line_tools_insufficient =
     when compiling for iOS, tvOS, visionOS or watchOS, you need a full installation of Xcode
 
diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs
index 2f68bad1695..2274450e20e 100644
--- a/compiler/rustc_codegen_ssa/src/back/apple.rs
+++ b/compiler/rustc_codegen_ssa/src/back/apple.rs
@@ -160,6 +160,10 @@ pub(super) fn add_version_to_llvm_target(
 pub(super) fn get_sdk_root(sess: &Session) -> Option<PathBuf> {
     let sdk_name = sdk_name(&sess.target);
 
+    // Attempt to invoke `xcrun` to find the SDK.
+    //
+    // Note that when cross-compiling from e.g. Linux, the `xcrun` binary may sometimes be provided
+    // as a shim by a cross-compilation helper tool. It usually isn't, but we still try nonetheless.
     match xcrun_show_sdk_path(sdk_name, sess.verbose_internals()) {
         Ok((path, stderr)) => {
             // Emit extra stderr, such as if `-verbose` was passed, or if `xcrun` emitted a warning.
@@ -169,7 +173,19 @@ pub(super) fn get_sdk_root(sess: &Session) -> Option<PathBuf> {
             Some(path)
         }
         Err(err) => {
-            let mut diag = sess.dcx().create_err(err);
+            // Failure to find the SDK is not a hard error, since the user might have specified it
+            // in a manner unknown to us (moreso if cross-compiling):
+            // - A compiler driver like `zig cc` which links using an internally bundled SDK.
+            // - Extra linker arguments (`-Clink-arg=-syslibroot`).
+            // - A custom linker or custom compiler driver.
+            //
+            // Though we still warn, since such cases are uncommon, and it is very hard to debug if
+            // you do not know the details.
+            //
+            // FIXME(madsmtm): Make this a lint, to allow deny warnings to work.
+            // (Or fix <https://github.com/rust-lang/rust/issues/21204>).
+            let mut diag = sess.dcx().create_warn(err);
+            diag.note(fluent::codegen_ssa_xcrun_about);
 
             // Recognize common error cases, and give more Rust-specific error messages for those.
             if let Some(developer_dir) = xcode_select_developer_dir() {
@@ -209,6 +225,8 @@ fn xcrun_show_sdk_path(
     sdk_name: &'static str,
     verbose: bool,
 ) -> Result<(PathBuf, String), XcrunError> {
+    // Intentionally invoke the `xcrun` in PATH, since e.g. nixpkgs provide an `xcrun` shim, so we
+    // don't want to require `/usr/bin/xcrun`.
     let mut cmd = Command::new("xcrun");
     if verbose {
         cmd.arg("--verbose");
@@ -280,7 +298,7 @@ fn stdout_to_path(mut stdout: Vec<u8>) -> PathBuf {
     }
     #[cfg(unix)]
     let path = <OsString as std::os::unix::ffi::OsStringExt>::from_vec(stdout);
-    #[cfg(not(unix))] // Unimportant, this is only used on macOS
-    let path = OsString::from(String::from_utf8(stdout).unwrap());
+    #[cfg(not(unix))] // Not so important, this is mostly used on macOS
+    let path = OsString::from(String::from_utf8(stdout).expect("stdout must be UTF-8"));
     PathBuf::from(path)
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 3ec0d900994..4ebe59dc2a7 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -3194,39 +3194,60 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
 }
 
 fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> Option<PathBuf> {
-    let os = &sess.target.os;
-    if sess.target.vendor != "apple"
-        || !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "visionos" | "macos")
-        || !matches!(flavor, LinkerFlavor::Darwin(..))
-    {
+    if !sess.target.is_like_darwin {
         return None;
     }
-
-    if os == "macos" && !matches!(flavor, LinkerFlavor::Darwin(Cc::No, _)) {
+    let LinkerFlavor::Darwin(cc, _) = flavor else {
         return None;
-    }
-
-    let sdk_root = sess.time("get_apple_sdk_root", || get_apple_sdk_root(sess))?;
+    };
 
-    match flavor {
-        LinkerFlavor::Darwin(Cc::Yes, _) => {
-            // Use `-isysroot` instead of `--sysroot`, as only the former
-            // makes Clang treat it as a platform SDK.
-            //
-            // This is admittedly a bit strange, as on most targets
-            // `-isysroot` only applies to include header files, but on Apple
-            // targets this also applies to libraries and frameworks.
-            cmd.cc_arg("-isysroot");
-            cmd.cc_arg(&sdk_root);
-        }
-        LinkerFlavor::Darwin(Cc::No, _) => {
-            cmd.link_arg("-syslibroot");
-            cmd.link_arg(&sdk_root);
-        }
-        _ => unreachable!(),
+    // The default compiler driver on macOS is at `/usr/bin/cc`. This is a trampoline binary that
+    // effectively invokes `xcrun cc` internally to look up both the compiler binary and the SDK
+    // root from the current Xcode installation. When cross-compiling, when `rustc` is invoked
+    // inside Xcode, or when invoking the linker directly, this default logic is unsuitable, so
+    // instead we invoke `xcrun` manually.
+    //
+    // (Note that this doesn't mean we get a duplicate lookup here - passing `SDKROOT` below will
+    // cause the trampoline binary to skip looking up the SDK itself).
+    let sdkroot = sess.time("get_apple_sdk_root", || get_apple_sdk_root(sess))?;
+
+    if cc == Cc::Yes {
+        // There are a few options to pass the SDK root when linking with a C/C++ compiler:
+        // - The `--sysroot` flag.
+        // - The `-isysroot` flag.
+        // - The `SDKROOT` environment variable.
+        //
+        // `--sysroot` isn't actually enough to get Clang to treat it as a platform SDK, you need
+        // to specify `-isysroot`. This is admittedly a bit strange, as on most targets `-isysroot`
+        // only applies to include header files, but on Apple targets it also applies to libraries
+        // and frameworks.
+        //
+        // This leaves the choice between `-isysroot` and `SDKROOT`. Both are supported by Clang and
+        // GCC, though they may not be supported by all compiler drivers. We choose `SDKROOT`,
+        // primarily because that is the same interface that is used when invoking the tool under
+        // `xcrun -sdk macosx $tool`.
+        //
+        // In that sense, if a given compiler driver does not support `SDKROOT`, the blame is fairly
+        // clearly in the tool in question, since they also don't support being run under `xcrun`.
+        //
+        // Additionally, `SDKROOT` is an environment variable and thus optional. It also has lower
+        // precedence than `-isysroot`, so a custom compiler driver that does not support it and
+        // instead figures out the SDK on their own can easily do so by using `-isysroot`.
+        //
+        // (This in particular affects Clang built with the `DEFAULT_SYSROOT` CMake flag, such as
+        // the one provided by some versions of Homebrew's `llvm` package. Those will end up
+        // ignoring the value we set here, and instead use their built-in sysroot).
+        cmd.cmd().env("SDKROOT", &sdkroot);
+    } else {
+        // When invoking the linker directly, we use the `-syslibroot` parameter. `SDKROOT` is not
+        // read by the linker, so it's really the only option.
+        //
+        // This is also what Clang does.
+        cmd.link_arg("-syslibroot");
+        cmd.link_arg(&sdkroot);
     }
 
-    Some(sdk_root)
+    Some(sdkroot)
 }
 
 fn get_apple_sdk_root(sess: &Session) -> Option<PathBuf> {
@@ -3255,7 +3276,13 @@ fn get_apple_sdk_root(sess: &Session) -> Option<PathBuf> {
             }
             "macosx"
                 if sdkroot.contains("iPhoneOS.platform")
-                    || sdkroot.contains("iPhoneSimulator.platform") => {}
+                    || sdkroot.contains("iPhoneSimulator.platform")
+                    || sdkroot.contains("AppleTVOS.platform")
+                    || sdkroot.contains("AppleTVSimulator.platform")
+                    || sdkroot.contains("WatchOS.platform")
+                    || sdkroot.contains("WatchSimulator.platform")
+                    || sdkroot.contains("XROS.platform")
+                    || sdkroot.contains("XRSimulator.platform") => {}
             "watchos"
                 if sdkroot.contains("WatchSimulator.platform")
                     || sdkroot.contains("MacOSX.platform") => {}
diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs
index a0160d1188d..b1cc0cc2878 100644
--- a/compiler/rustc_const_eval/src/interpret/call.rs
+++ b/compiler/rustc_const_eval/src/interpret/call.rs
@@ -346,7 +346,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         destination: &PlaceTy<'tcx, M::Provenance>,
         mut cont: ReturnContinuation,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty);
+        let _trace = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty);
 
         // Compute callee information.
         // FIXME: for variadic support, do we have to somehow determine callee's extra_args?
@@ -527,7 +527,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         target: Option<mir::BasicBlock>,
         unwind: mir::UnwindAction,
     ) -> InterpResult<'tcx> {
-        let _span =
+        let _trace =
             enter_trace_span!(M, step::init_fn_call, tracing_separate_thread = Empty, ?fn_val)
                 .or_if_tracing_disabled(|| trace!("init_fn_call: {:#?}", fn_val));
 
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index c4b705d7124..d4f2bb8257d 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -113,7 +113,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// See [LayoutOf::layout_of] for the original documentation.
     #[inline(always)]
     pub fn layout_of(&self, ty: Ty<'tcx>) -> <Self as LayoutOfHelpers<'tcx>>::LayoutOfResult {
-        let _span = enter_trace_span!(M, layouting::layout_of, ty = ?ty.kind());
+        let _trace = enter_trace_span!(M, layouting::layout_of, ty = ?ty.kind());
         LayoutOf::layout_of(self, ty)
     }
 
@@ -126,7 +126,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         sig: ty::PolyFnSig<'tcx>,
         extra_args: &'tcx ty::List<Ty<'tcx>>,
     ) -> <Self as FnAbiOfHelpers<'tcx>>::FnAbiOfResult {
-        let _span = enter_trace_span!(M, layouting::fn_abi_of_fn_ptr, ?sig, ?extra_args);
+        let _trace = enter_trace_span!(M, layouting::fn_abi_of_fn_ptr, ?sig, ?extra_args);
         FnAbiOf::fn_abi_of_fn_ptr(self, sig, extra_args)
     }
 
@@ -139,7 +139,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         instance: ty::Instance<'tcx>,
         extra_args: &'tcx ty::List<Ty<'tcx>>,
     ) -> <Self as FnAbiOfHelpers<'tcx>>::FnAbiOfResult {
-        let _span = enter_trace_span!(M, layouting::fn_abi_of_instance, ?instance, ?extra_args);
+        let _trace = enter_trace_span!(M, layouting::fn_abi_of_instance, ?instance, ?extra_args);
         FnAbiOf::fn_abi_of_instance(self, instance, extra_args)
     }
 }
@@ -322,7 +322,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
         value: T,
     ) -> Result<T, ErrorHandled> {
-        let _span = enter_trace_span!(
+        let _trace = enter_trace_span!(
             M,
             "instantiate_from_frame_and_normalize_erasing_regions",
             "{}",
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 14541809070..53a440b646b 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -773,7 +773,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         mir_place: mir::Place<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        let _span = enter_trace_span!(
+        let _trace = enter_trace_span!(
             M,
             step::eval_place_to_op,
             ?mir_place,
@@ -823,7 +823,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         mir_op: &mir::Operand<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
-        let _span =
+        let _trace =
             enter_trace_span!(M, step::eval_operand, ?mir_op, tracing_separate_thread = Empty);
 
         use rustc_middle::mir::Operand::*;
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 45c4edb8503..6ff50dc700f 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -526,7 +526,7 @@ where
         &self,
         mir_place: mir::Place<'tcx>,
     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> {
-        let _span =
+        let _trace =
             enter_trace_span!(M, step::eval_place, ?mir_place, tracing_separate_thread = Empty);
 
         let mut place = self.local_to_place(mir_place.local)?;
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 9df49c0f4cc..76e470b69dc 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -76,7 +76,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     ///
     /// This does NOT move the statement counter forward, the caller has to do that!
     pub fn eval_statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(
+        let _trace = enter_trace_span!(
             M,
             step::eval_statement,
             stmt = ?stmt.kind,
@@ -465,7 +465,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     }
 
     fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(
+        let _trace = enter_trace_span!(
             M,
             step::eval_terminator,
             terminator = ?terminator.kind,
diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs
index 71800950faa..72bee345406 100644
--- a/compiler/rustc_const_eval/src/interpret/util.rs
+++ b/compiler/rustc_const_eval/src/interpret/util.rs
@@ -85,11 +85,11 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan {
 /// # let my_debug_var = String::new();
 /// // logs a span named "hello" with a field named "arg" of value 42 (works only because
 /// // 42 implements the tracing::Value trait, otherwise use one of the options below)
-/// let _span = enter_trace_span!(M, "hello", arg = 42);
+/// let _trace = enter_trace_span!(M, "hello", arg = 42);
 /// // logs a field called "my_display_var" using the Display implementation
-/// let _span = enter_trace_span!(M, "hello", %my_display_var);
+/// let _trace = enter_trace_span!(M, "hello", %my_display_var);
 /// // logs a field called "my_debug_var" using the Debug implementation
-/// let _span = enter_trace_span!(M, "hello", ?my_debug_var);
+/// let _trace = enter_trace_span!(M, "hello", ?my_debug_var);
 ///  ```
 ///
 /// ### `NAME::SUBNAME` syntax
@@ -107,8 +107,8 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan {
 /// # use rustc_const_eval::enter_trace_span;
 /// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
 /// // for example, the first will expand to the second
-/// let _span = enter_trace_span!(M, borrow_tracker::on_stack_pop, /* ... */);
-/// let _span = enter_trace_span!(M, "borrow_tracker", borrow_tracker = "on_stack_pop", /* ... */);
+/// let _trace = enter_trace_span!(M, borrow_tracker::on_stack_pop, /* ... */);
+/// let _trace = enter_trace_span!(M, "borrow_tracker", borrow_tracker = "on_stack_pop", /* ... */);
 /// ```
 ///
 /// ### `tracing_separate_thread` parameter
@@ -124,7 +124,7 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan {
 /// ```rust
 /// # use rustc_const_eval::enter_trace_span;
 /// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
-/// let _span = enter_trace_span!(M, step::eval_statement, tracing_separate_thread = tracing::field::Empty);
+/// let _trace = enter_trace_span!(M, step::eval_statement, tracing_separate_thread = tracing::field::Empty);
 /// ```
 ///
 /// ### Executing something else when tracing is disabled
@@ -136,7 +136,7 @@ impl EnteredTraceSpan for tracing::span::EnteredSpan {
 /// # use rustc_const_eval::enter_trace_span;
 /// # use rustc_const_eval::interpret::EnteredTraceSpan;
 /// # type M = rustc_const_eval::const_eval::CompileTimeMachine<'static>;
-/// let _span = enter_trace_span!(M, step::eval_statement)
+/// let _trace = enter_trace_span!(M, step::eval_statement)
 ///     .or_if_tracing_disabled(|| tracing::info!("eval_statement"));
 /// ```
 #[macro_export]
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index ed48f53c310..ab0c0665d51 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -1415,7 +1415,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         recursive: bool,
         reset_provenance_and_padding: bool,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(
+        let _trace = enter_trace_span!(
             M,
             "validate_operand",
             "recursive={recursive}, reset_provenance_and_padding={reset_provenance_and_padding}, val={val:?}"
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index c234aa43c09..7da3bf27eb5 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -904,10 +904,7 @@ impl SyntaxExtension {
             find_attr!(attrs, AttributeKind::AllowInternalUnstable(i, _) => i)
                 .map(|i| i.as_slice())
                 .unwrap_or_default();
-        // FIXME(jdonszelman): allow_internal_unsafe isn't yet new-style
-        // let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe);
-        let allow_internal_unsafe =
-            ast::attr::find_by_name(attrs, sym::allow_internal_unsafe).is_some();
+        let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe(_));
 
         let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export)
             .and_then(|macro_export| macro_export.meta_item_list())
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 5c63d4808db..ab6b8f92802 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -120,13 +120,15 @@ pub struct AttributeTemplate {
     /// If `true`, the attribute is allowed to be a bare word like `#[test]`.
     pub word: bool,
     /// If `Some`, the attribute is allowed to take a list of items like `#[allow(..)]`.
-    pub list: Option<&'static str>,
+    pub list: Option<&'static [&'static str]>,
     /// If non-empty, the attribute is allowed to take a list containing exactly
     /// one of the listed words, like `#[coverage(off)]`.
     pub one_of: &'static [Symbol],
     /// If `Some`, the attribute is allowed to be a name/value pair where the
     /// value is a string, like `#[must_use = "reason"]`.
-    pub name_value_str: Option<&'static str>,
+    pub name_value_str: Option<&'static [&'static str]>,
+    /// A link to the document for this attribute.
+    pub docs: Option<&'static str>,
 }
 
 impl AttributeTemplate {
@@ -137,11 +139,15 @@ impl AttributeTemplate {
             suggestions.push(format!("#{inner}[{name}]"));
         }
         if let Some(descr) = self.list {
-            suggestions.push(format!("#{inner}[{name}({descr})]"));
+            for descr in descr {
+                suggestions.push(format!("#{inner}[{name}({descr})]"));
+            }
         }
         suggestions.extend(self.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
         if let Some(descr) = self.name_value_str {
-            suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
+            for descr in descr {
+                suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
+            }
         }
         suggestions.sort();
 
@@ -205,20 +211,33 @@ pub enum AttributeDuplicates {
 /// supports forms `#[attr]` and `#[attr(description)]`.
 #[macro_export]
 macro_rules! template {
-    (Word) => { $crate::template!(@ true, None, &[], None) };
-    (List: $descr: expr) => { $crate::template!(@ false, Some($descr), &[], None) };
-    (OneOf: $one_of: expr) => { $crate::template!(@ false, None, $one_of, None) };
-    (NameValueStr: $descr: expr) => { $crate::template!(@ false, None, &[], Some($descr)) };
-    (Word, List: $descr: expr) => { $crate::template!(@ true, Some($descr), &[], None) };
-    (Word, NameValueStr: $descr: expr) => { $crate::template!(@ true, None, &[], Some($descr)) };
+    (Word) => { $crate::template!(@ true, None, &[], None, None) };
+    (Word, $link: literal) => { $crate::template!(@ true, None, &[], None, Some($link)) };
+    (List: $descr: expr) => { $crate::template!(@ false, Some($descr), &[], None, None) };
+    (List: $descr: expr, $link: literal) => { $crate::template!(@ false, Some($descr), &[], None, Some($link)) };
+    (OneOf: $one_of: expr) => { $crate::template!(@ false, None, $one_of, None, None) };
+    (NameValueStr: [$($descr: literal),* $(,)?]) => { $crate::template!(@ false, None, &[], Some(&[$($descr,)*]), None) };
+    (NameValueStr: [$($descr: literal),* $(,)?], $link: literal) => { $crate::template!(@ false, None, &[], Some(&[$($descr,)*]), Some($link)) };
+    (NameValueStr: $descr: literal) => { $crate::template!(@ false, None, &[], Some(&[$descr]), None) };
+    (NameValueStr: $descr: literal, $link: literal) => { $crate::template!(@ false, None, &[], Some(&[$descr]), Some($link)) };
+    (Word, List: $descr: expr) => { $crate::template!(@ true, Some($descr), &[], None, None) };
+    (Word, List: $descr: expr, $link: literal) => { $crate::template!(@ true, Some($descr), &[], None, Some($link)) };
+    (Word, NameValueStr: $descr: expr) => { $crate::template!(@ true, None, &[], Some(&[$descr]), None) };
+    (Word, NameValueStr: $descr: expr, $link: literal) => { $crate::template!(@ true, None, &[], Some(&[$descr]), Some($link)) };
     (List: $descr1: expr, NameValueStr: $descr2: expr) => {
-        $crate::template!(@ false, Some($descr1), &[], Some($descr2))
+        $crate::template!(@ false, Some($descr1), &[], Some(&[$descr2]), None)
+    };
+    (List: $descr1: expr, NameValueStr: $descr2: expr, $link: literal) => {
+        $crate::template!(@ false, Some($descr1), &[], Some(&[$descr2]), Some($link))
     };
     (Word, List: $descr1: expr, NameValueStr: $descr2: expr) => {
-        $crate::template!(@ true, Some($descr1), &[], Some($descr2))
+        $crate::template!(@ true, Some($descr1), &[], Some(&[$descr2]), None)
     };
-    (@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr) => { $crate::AttributeTemplate {
-        word: $word, list: $list, one_of: $one_of, name_value_str: $name_value_str
+    (Word, List: $descr1: expr, NameValueStr: $descr2: expr, $link: literal) => {
+        $crate::template!(@ true, Some($descr1), &[], Some(&[$descr2]), Some($link))
+    };
+    (@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr, $link: expr) => { $crate::AttributeTemplate {
+        word: $word, list: $list, one_of: $one_of, name_value_str: $name_value_str, docs: $link,
     } };
 }
 
@@ -391,18 +410,42 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // ==========================================================================
 
     // Conditional compilation:
-    ungated!(cfg, Normal, template!(List: "predicate"), DuplicatesOk, EncodeCrossCrate::Yes),
-    ungated!(cfg_attr, Normal, template!(List: "predicate, attr1, attr2, ..."), DuplicatesOk, EncodeCrossCrate::Yes),
+    ungated!(
+        cfg, Normal,
+        template!(
+            List: &["predicate"],
+            "https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute"
+        ),
+        DuplicatesOk, EncodeCrossCrate::Yes
+    ),
+    ungated!(
+        cfg_attr, Normal,
+        template!(
+            List: &["predicate, attr1, attr2, ..."],
+            "https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute"
+        ),
+        DuplicatesOk, EncodeCrossCrate::Yes
+    ),
 
     // Testing:
     ungated!(
-        ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing,
-        EncodeCrossCrate::No,
+        ignore, Normal,
+        template!(
+            Word,
+            NameValueStr: "reason",
+            "https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute"
+        ),
+        WarnFollowing, EncodeCrossCrate::No,
     ),
     ungated!(
         should_panic, Normal,
-        template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing,
-        EncodeCrossCrate::No,
+        template!(
+            Word,
+            List: &[r#"expected = "reason""#],
+            NameValueStr: "reason",
+            "https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute"
+        ),
+        FutureWarnFollowing, EncodeCrossCrate::No,
     ),
     // FIXME(Centril): This can be used on stable but shouldn't.
     ungated!(
@@ -411,46 +454,102 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
 
     // Macros:
-    ungated!(automatically_derived, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes),
     ungated!(
-        macro_use, Normal, template!(Word, List: "name1, name2, ..."), WarnFollowingWordOnly,
-        EncodeCrossCrate::No,
+        automatically_derived, Normal,
+        template!(
+            Word,
+            "https://doc.rust-lang.org/reference/attributes/derive.html#the-automatically_derived-attribute"
+        ),
+        WarnFollowing, EncodeCrossCrate::Yes
+    ),
+    ungated!(
+        macro_use, Normal,
+        template!(
+            Word,
+            List: &["name1, name2, ..."],
+            "https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute"
+        ),
+        WarnFollowingWordOnly, EncodeCrossCrate::No,
     ),
     ungated!(macro_escape, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), // Deprecated synonym for `macro_use`.
     ungated!(
-        macro_export, Normal, template!(Word, List: "local_inner_macros"),
+        macro_export, Normal,
+        template!(
+            Word,
+            List: &["local_inner_macros"],
+            "https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope"
+        ),
         WarnFollowing, EncodeCrossCrate::Yes
     ),
-    ungated!(proc_macro, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No),
     ungated!(
-        proc_macro_derive, Normal, template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"),
+        proc_macro, Normal,
+        template!(
+            Word,
+            "https://doc.rust-lang.org/reference/procedural-macros.html#function-like-procedural-macros"),
+        ErrorFollowing, EncodeCrossCrate::No
+    ),
+    ungated!(
+        proc_macro_derive, Normal,
+        template!(
+            List: &["TraitName", "TraitName, attributes(name1, name2, ...)"],
+            "https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros"
+        ),
         ErrorFollowing, EncodeCrossCrate::No,
     ),
-    ungated!(proc_macro_attribute, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No),
+    ungated!(
+        proc_macro_attribute, Normal,
+        template!(Word, "https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros"),
+        ErrorFollowing, EncodeCrossCrate::No
+    ),
 
     // Lints:
     ungated!(
-        warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+        warn, Normal,
+        template!(
+            List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#],
+            "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
+        ),
         DuplicatesOk, EncodeCrossCrate::No,
     ),
     ungated!(
-        allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+        allow, Normal,
+        template!(
+            List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#],
+            "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
+        ),
         DuplicatesOk, EncodeCrossCrate::No,
     ),
     ungated!(
-        expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+        expect, Normal,
+        template!(
+            List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#],
+            "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
+        ),
         DuplicatesOk, EncodeCrossCrate::No,
     ),
     ungated!(
-        forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+        forbid, Normal,
+        template!(
+            List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#],
+            "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
+        ),
         DuplicatesOk, EncodeCrossCrate::No
     ),
     ungated!(
-        deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+        deny, Normal,
+        template!(
+            List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#],
+            "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
+        ),
         DuplicatesOk, EncodeCrossCrate::No
     ),
     ungated!(
-        must_use, Normal, template!(Word, NameValueStr: "reason"),
+        must_use, Normal,
+        template!(
+            Word,
+            NameValueStr: "reason",
+            "https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute"
+        ),
         FutureWarnFollowing, EncodeCrossCrate::Yes
     ),
     gated!(
@@ -461,52 +560,104 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         deprecated, Normal,
         template!(
             Word,
-            List: r#"/*opt*/ since = "version", /*opt*/ note = "reason""#,
-            NameValueStr: "reason"
+            List: &[r#"/*opt*/ since = "version", /*opt*/ note = "reason""#],
+            NameValueStr: "reason",
+            "https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute"
         ),
         ErrorFollowing, EncodeCrossCrate::Yes
     ),
 
     // Crate properties:
     ungated!(
-        crate_name, CrateLevel, template!(NameValueStr: "name"), FutureWarnFollowing,
-        EncodeCrossCrate::No,
+        crate_name, CrateLevel,
+        template!(
+            NameValueStr: "name",
+            "https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute"
+        ),
+        FutureWarnFollowing, EncodeCrossCrate::No,
     ),
     ungated!(
-        crate_type, CrateLevel, template!(NameValueStr: "bin|lib|..."), DuplicatesOk,
-        EncodeCrossCrate::No,
+        crate_type, CrateLevel,
+        template!(
+            NameValueStr: ["bin", "lib", "dylib", "cdylib", "rlib", "staticlib", "sdylib", "proc-macro"],
+            "https://doc.rust-lang.org/reference/linkage.html"
+        ),
+        DuplicatesOk, EncodeCrossCrate::No,
     ),
 
     // ABI, linking, symbols, and FFI
     ungated!(
         link, Normal,
-        template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#),
-        DuplicatesOk,
-        EncodeCrossCrate::No,
+        template!(List: &[
+            r#"name = "...""#,
+            r#"name = "...", kind = "dylib|static|...""#,
+            r#"name = "...", wasm_import_module = "...""#,
+            r#"name = "...", import_name_type = "decorated|noprefix|undecorated""#,
+            r#"name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated""#,
+        ], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute"),
+        DuplicatesOk, EncodeCrossCrate::No,
     ),
     ungated!(
-        link_name, Normal, template!(NameValueStr: "name"),
+        link_name, Normal,
+        template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"),
         FutureWarnPreceding, EncodeCrossCrate::Yes
     ),
-    ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
-    ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No),
+    ungated!(
+        no_link, Normal,
+        template!(Word, "https://doc.rust-lang.org/reference/items/extern-crates.html#the-no_link-attribute"),
+        WarnFollowing, EncodeCrossCrate::No
+    ),
+    ungated!(
+        repr, Normal,
+        template!(
+            List: &["C", "Rust", "transparent", "align(...)", "packed(...)", "<integer type>"],
+            "https://doc.rust-lang.org/reference/type-layout.html#representations"
+        ),
+        DuplicatesOk, EncodeCrossCrate::No
+    ),
     // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
-    gated!(rustc_align, Normal, template!(List: "alignment"), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(rustc_align)),
-    ungated!(unsafe(Edition2024) export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
-    ungated!(unsafe(Edition2024) link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
-    ungated!(unsafe(Edition2024) no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
-    ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No),
-    ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes),
-    ungated!(unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
+    gated!(rustc_align, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(rustc_align)),
+    ungated!(
+        unsafe(Edition2024) export_name, Normal,
+        template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute"),
+        FutureWarnPreceding, EncodeCrossCrate::No
+    ),
+    ungated!(
+        unsafe(Edition2024) link_section, Normal,
+        template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"),
+        FutureWarnPreceding, EncodeCrossCrate::No
+    ),
+    ungated!(
+        unsafe(Edition2024) no_mangle, Normal,
+        template!(Word, "https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute"),
+        WarnFollowing, EncodeCrossCrate::No
+    ),
+    ungated!(
+        used, Normal,
+        template!(Word, List: &["compiler", "linker"], "https://doc.rust-lang.org/reference/abi.html#the-used-attribute"),
+        WarnFollowing, EncodeCrossCrate::No
+    ),
+    ungated!(
+        link_ordinal, Normal,
+        template!(List: &["ordinal"], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"),
+        ErrorPreceding, EncodeCrossCrate::Yes
+    ),
+    ungated!(
+        unsafe naked, Normal,
+        template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-naked-attribute"),
+        WarnFollowing, EncodeCrossCrate::No
+    ),
 
     // Limits:
     ungated!(
-        recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing,
-        EncodeCrossCrate::No
+        recursion_limit, CrateLevel,
+        template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"),
+        FutureWarnFollowing, EncodeCrossCrate::No
     ),
     ungated!(
-        type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing,
-        EncodeCrossCrate::No
+        type_length_limit, CrateLevel,
+        template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-type_length_limit-attribute"),
+        FutureWarnFollowing, EncodeCrossCrate::No
     ),
     gated!(
         move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
@@ -514,35 +665,84 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
 
     // Entry point:
-    ungated!(no_main, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No),
+    ungated!(
+        no_main, CrateLevel,
+        template!(Word, "https://doc.rust-lang.org/reference/crates-and-source-files.html#the-no_main-attribute"),
+        WarnFollowing, EncodeCrossCrate::No
+    ),
 
     // Modules, prelude, and resolution:
-    ungated!(path, Normal, template!(NameValueStr: "file"), FutureWarnFollowing, EncodeCrossCrate::No),
-    ungated!(no_std, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No),
-    ungated!(no_implicit_prelude, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
-    ungated!(non_exhaustive, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes),
+    ungated!(
+        path, Normal,
+        template!(NameValueStr: "file", "https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute"),
+        FutureWarnFollowing, EncodeCrossCrate::No
+    ),
+    ungated!(
+        no_std, CrateLevel,
+        template!(Word, "https://doc.rust-lang.org/reference/names/preludes.html#the-no_std-attribute"),
+        WarnFollowing, EncodeCrossCrate::No
+    ),
+    ungated!(
+        no_implicit_prelude, Normal,
+        template!(Word, "https://doc.rust-lang.org/reference/names/preludes.html#the-no_implicit_prelude-attribute"),
+        WarnFollowing, EncodeCrossCrate::No
+    ),
+    ungated!(
+        non_exhaustive, Normal,
+        template!(Word, "https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute"),
+        WarnFollowing, EncodeCrossCrate::Yes
+    ),
 
     // Runtime
     ungated!(
         windows_subsystem, CrateLevel,
-        template!(NameValueStr: "windows|console"), FutureWarnFollowing,
-        EncodeCrossCrate::No
+        template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute"),
+        FutureWarnFollowing, EncodeCrossCrate::No
+    ),
+    ungated!( // RFC 2070
+        panic_handler, Normal,
+        template!(Word, "https://doc.rust-lang.org/reference/panic.html#the-panic_handler-attribute"),
+        WarnFollowing, EncodeCrossCrate::Yes
     ),
-    ungated!(panic_handler, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes), // RFC 2070
 
     // Code generation:
-    ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, EncodeCrossCrate::No),
-    ungated!(cold, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
-    ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::Yes),
     ungated!(
-        target_feature, Normal, template!(List: r#"enable = "name""#),
+        inline, Normal,
+        template!(
+            Word,
+            List: &["always", "never"],
+            "https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute"
+        ),
+        FutureWarnFollowing, EncodeCrossCrate::No
+    ),
+    ungated!(
+        cold, Normal,
+        template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-cold-attribute"),
+        WarnFollowing, EncodeCrossCrate::No
+    ),
+    ungated!(
+        no_builtins, CrateLevel,
+        template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-no_builtins-attribute"),
+        WarnFollowing, EncodeCrossCrate::Yes
+    ),
+    ungated!(
+        target_feature, Normal,
+        template!(List: &[r#"enable = "name""#], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-target_feature-attribute"),
         DuplicatesOk, EncodeCrossCrate::No,
     ),
-    ungated!(track_caller, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes),
-    ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding, EncodeCrossCrate::No),
+    ungated!(
+        track_caller, Normal,
+        template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-track_caller-attribute"),
+        WarnFollowing, EncodeCrossCrate::Yes
+    ),
+    ungated!(
+        instruction_set, Normal,
+        template!(List: &["set"], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute"),
+        ErrorPreceding, EncodeCrossCrate::No
+    ),
     gated!(
         no_sanitize, Normal,
-        template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
+        template!(List: &["address, kcfi, memory, thread"]), DuplicatesOk,
         EncodeCrossCrate::No, experimental!(no_sanitize)
     ),
     gated!(
@@ -552,18 +752,31 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
 
     ungated!(
-        doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk,
-        EncodeCrossCrate::Yes
+        doc, Normal,
+        template!(
+            List: &["hidden", "inline"],
+            NameValueStr: "string",
+            "https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html"
+        ),
+        DuplicatesOk, EncodeCrossCrate::Yes
     ),
 
     // Debugging
     ungated!(
         debugger_visualizer, Normal,
-        template!(List: r#"natvis_file = "...", gdb_script_file = "...""#),
+        template!(
+            List: &[r#"natvis_file = "...", gdb_script_file = "...""#],
+            "https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute"
+        ),
         DuplicatesOk, EncodeCrossCrate::No
     ),
-    ungated!(collapse_debuginfo, Normal, template!(List: "no|external|yes"), ErrorFollowing,
-        EncodeCrossCrate::Yes
+    ungated!(
+        collapse_debuginfo, Normal,
+        template!(
+            List: &["no", "external", "yes"],
+            "https://doc.rust-lang.org/reference/attributes/debugger.html#the-collapse_debuginfo-attribute"
+        ),
+        ErrorFollowing, EncodeCrossCrate::Yes
     ),
 
     // ==========================================================================
@@ -578,7 +791,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
 
     // Testing:
     gated!(
-        test_runner, CrateLevel, template!(List: "path"), ErrorFollowing,
+        test_runner, CrateLevel, template!(List: &["path"]), ErrorFollowing,
         EncodeCrossCrate::Yes, custom_test_frameworks,
         "custom test frameworks are an unstable feature",
     ),
@@ -597,7 +810,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     // RFC 2412
     gated!(
-        optimize, Normal, template!(List: "none|size|speed"), ErrorPreceding,
+        optimize, Normal, template!(List: &["none", "size", "speed"]), ErrorPreceding,
         EncodeCrossCrate::No, optimize_attribute, experimental!(optimize)
     ),
 
@@ -610,7 +823,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         EncodeCrossCrate::No, experimental!(ffi_const)
     ),
     gated!(
-        register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk,
+        register_tool, CrateLevel, template!(List: &["tool1, tool2, ..."]), DuplicatesOk,
         EncodeCrossCrate::No, experimental!(register_tool),
     ),
 
@@ -624,7 +837,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     // lang-team MCP 147
     gated!(
-        deprecated_safe, Normal, template!(List: r#"since = "version", note = "...""#), ErrorFollowing,
+        deprecated_safe, Normal, template!(List: &[r#"since = "version", note = "...""#]), ErrorFollowing,
         EncodeCrossCrate::Yes, experimental!(deprecated_safe),
     ),
 
@@ -643,7 +856,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // RFC 3543
     // `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]`
     gated!(
-        patchable_function_entry, Normal, template!(List: "prefix_nops = m, entry_nops = n"), ErrorPreceding,
+        patchable_function_entry, Normal, template!(List: &["prefix_nops = m, entry_nops = n"]), ErrorPreceding,
         EncodeCrossCrate::Yes, experimental!(patchable_function_entry)
     ),
 
@@ -673,37 +886,37 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
 
     ungated!(
         feature, CrateLevel,
-        template!(List: "name1, name2, ..."), DuplicatesOk, EncodeCrossCrate::No,
+        template!(List: &["name1, name2, ..."]), DuplicatesOk, EncodeCrossCrate::No,
     ),
     // DuplicatesOk since it has its own validation
     ungated!(
         stable, Normal,
-        template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, EncodeCrossCrate::No,
+        template!(List: &[r#"feature = "name", since = "version""#]), DuplicatesOk, EncodeCrossCrate::No,
     ),
     ungated!(
         unstable, Normal,
-        template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
+        template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]), DuplicatesOk,
         EncodeCrossCrate::Yes
     ),
     ungated!(
-        unstable_feature_bound, Normal, template!(Word, List: "feat1, feat2, ..."),
+        unstable_feature_bound, Normal, template!(Word, List: &["feat1, feat2, ..."]),
         DuplicatesOk, EncodeCrossCrate::No,
     ),
     ungated!(
-        rustc_const_unstable, Normal, template!(List: r#"feature = "name""#),
+        rustc_const_unstable, Normal, template!(List: &[r#"feature = "name""#]),
         DuplicatesOk, EncodeCrossCrate::Yes
     ),
     ungated!(
         rustc_const_stable, Normal,
-        template!(List: r#"feature = "name""#), DuplicatesOk, EncodeCrossCrate::No,
+        template!(List: &[r#"feature = "name""#]), DuplicatesOk, EncodeCrossCrate::No,
     ),
     ungated!(
         rustc_default_body_unstable, Normal,
-        template!(List: r#"feature = "name", reason = "...", issue = "N""#),
+        template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]),
         DuplicatesOk, EncodeCrossCrate::No
     ),
     gated!(
-        allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."),
+        allow_internal_unstable, Normal, template!(Word, List: &["feat1, feat2, ..."]),
         DuplicatesOk, EncodeCrossCrate::Yes,
         "allow_internal_unstable side-steps feature gating and stability checks",
     ),
@@ -718,7 +931,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         through unstable paths"
     ),
     rustc_attr!(
-        rustc_deprecated_safe_2024, Normal, template!(List: r#"audit_that = "...""#),
+        rustc_deprecated_safe_2024, Normal, template!(List: &[r#"audit_that = "...""#]),
         ErrorFollowing, EncodeCrossCrate::Yes,
         "`#[rustc_deprecated_safe_2024]` is used to declare functions unsafe across the edition 2024 boundary",
     ),
@@ -743,7 +956,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_never_type_options,
         Normal,
-        template!(List: r#"/*opt*/ fallback = "unit|niko|never|no""#),
+        template!(List: &[
+            "",
+            r#"fallback = "unit""#,
+            r#"fallback = "niko""#,
+            r#"fallback = "never""#,
+            r#"fallback = "no""#,
+        ]),
         ErrorFollowing,
         EncodeCrossCrate::No,
         "`rustc_never_type_options` is used to experiment with never type fallback and work on \
@@ -808,7 +1027,17 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // ==========================================================================
 
     gated!(
-        linkage, Normal, template!(NameValueStr: "external|internal|..."),
+        linkage, Normal, template!(NameValueStr: [
+            "available_externally",
+            "common",
+            "extern_weak",
+            "external",
+            "internal",
+            "linkonce",
+            "linkonce_odr",
+            "weak",
+            "weak_odr",
+        ], "https://doc.rust-lang.org/reference/linkage.html"),
         ErrorPreceding, EncodeCrossCrate::No,
         "the `linkage` attribute is experimental and not portable across platforms",
     ),
@@ -823,7 +1052,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
 
     rustc_attr!(
         rustc_builtin_macro, Normal,
-        template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"), ErrorFollowing,
+        template!(Word, List: &["name", "name, /*opt*/ attributes(name1, name2, ...)"]), ErrorFollowing,
         EncodeCrossCrate::Yes,
     ),
     rustc_attr!(
@@ -832,12 +1061,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     rustc_attr!(
         rustc_macro_transparency, Normal,
-        template!(NameValueStr: "transparent|semiopaque|opaque"), ErrorFollowing,
+        template!(NameValueStr: ["transparent", "semiopaque", "opaque"]), ErrorFollowing,
         EncodeCrossCrate::Yes, "used internally for testing macro hygiene",
     ),
     rustc_attr!(
         rustc_autodiff, Normal,
-        template!(Word, List: r#""...""#), DuplicatesOk,
+        template!(Word, List: &[r#""...""#]), DuplicatesOk,
         EncodeCrossCrate::Yes,
     ),
     // Traces that are left when `cfg` and `cfg_attr` attributes are expanded.
@@ -860,7 +1089,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(
         rustc_on_unimplemented, Normal,
         template!(
-            List: r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#,
+            List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#],
             NameValueStr: "message"
         ),
         ErrorFollowing, EncodeCrossCrate::Yes,
@@ -868,7 +1097,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     rustc_attr!(
         rustc_confusables, Normal,
-        template!(List: r#""name1", "name2", ..."#),
+        template!(List: &[r#""name1", "name2", ..."#]),
         ErrorFollowing, EncodeCrossCrate::Yes,
     ),
     // Enumerates "identity-like" conversion methods to suggest on type mismatch.
@@ -909,7 +1138,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // Used by the `rustc::bad_opt_access` lint on fields
     // types (as well as any others in future).
     rustc_attr!(
-        rustc_lint_opt_deny_field_access, Normal, template!(List: "message"),
+        rustc_lint_opt_deny_field_access, Normal, template!(List: &["message"]),
         WarnFollowing, EncodeCrossCrate::Yes,
     ),
 
@@ -921,7 +1150,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         rustc_promotable, Normal, template!(Word), WarnFollowing,
         EncodeCrossCrate::No, ),
     rustc_attr!(
-        rustc_legacy_const_generics, Normal, template!(List: "N"), ErrorFollowing,
+        rustc_legacy_const_generics, Normal, template!(List: &["N"]), ErrorFollowing,
         EncodeCrossCrate::Yes,
     ),
     // Do not const-check this function's body. It will always get replaced during CTFE via `hook_special_const_fn`.
@@ -946,7 +1175,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     gated!(
         rustc_allow_const_fn_unstable, Normal,
-        template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, EncodeCrossCrate::No,
+        template!(Word, List: &["feat1, feat2, ..."]), DuplicatesOk, EncodeCrossCrate::No,
         "rustc_allow_const_fn_unstable side-steps feature gating and stability checks"
     ),
 
@@ -955,13 +1184,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // ==========================================================================
 
     rustc_attr!(
-        rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"), ErrorFollowing,
+        rustc_layout_scalar_valid_range_start, Normal, template!(List: &["value"]), ErrorFollowing,
         EncodeCrossCrate::Yes,
         "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \
         niche optimizations in the standard library",
     ),
     rustc_attr!(
-        rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"), ErrorFollowing,
+        rustc_layout_scalar_valid_range_end, Normal, template!(List: &["value"]), ErrorFollowing,
         EncodeCrossCrate::Yes,
         "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
         niche optimizations in the standard library",
@@ -1097,14 +1326,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "the `#[rustc_main]` attribute is used internally to specify test entry point function",
     ),
     rustc_attr!(
-        rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), ErrorFollowing,
+        rustc_skip_during_method_dispatch, Normal, template!(List: &["array, boxed_slice"]), ErrorFollowing,
         EncodeCrossCrate::No,
         "the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \
         from method dispatch when the receiver is of the following type, for compatibility in \
         editions < 2021 (array) or editions < 2024 (boxed_slice)."
     ),
     rustc_attr!(
-        rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."),
+        rustc_must_implement_one_of, Normal, template!(List: &["function1, function2, ..."]),
         ErrorFollowing, EncodeCrossCrate::No,
         "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \
         definition of a trait. Its syntax and semantics are highly experimental and will be \
@@ -1166,11 +1395,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         WarnFollowing, EncodeCrossCrate::No
     ),
     rustc_attr!(
-        TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."),
+        TEST, rustc_layout, Normal, template!(List: &["field1, field2, ..."]),
         WarnFollowing, EncodeCrossCrate::Yes
     ),
     rustc_attr!(
-        TEST, rustc_abi, Normal, template!(List: "field1, field2, ..."),
+        TEST, rustc_abi, Normal, template!(List: &["field1, field2, ..."]),
         WarnFollowing, EncodeCrossCrate::No
     ),
     rustc_attr!(
@@ -1191,29 +1420,29 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         EncodeCrossCrate::Yes
     ),
     rustc_attr!(
-        TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode"), DuplicatesOk,
+        TEST, rustc_if_this_changed, Normal, template!(Word, List: &["DepNode"]), DuplicatesOk,
         EncodeCrossCrate::No
     ),
     rustc_attr!(
-        TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode"), DuplicatesOk,
+        TEST, rustc_then_this_would_need, Normal, template!(List: &["DepNode"]), DuplicatesOk,
         EncodeCrossCrate::No
     ),
     rustc_attr!(
         TEST, rustc_clean, Normal,
-        template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
+        template!(List: &[r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#]),
         DuplicatesOk, EncodeCrossCrate::No
     ),
     rustc_attr!(
         TEST, rustc_partition_reused, Normal,
-        template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, EncodeCrossCrate::No
+        template!(List: &[r#"cfg = "...", module = "...""#]), DuplicatesOk, EncodeCrossCrate::No
     ),
     rustc_attr!(
         TEST, rustc_partition_codegened, Normal,
-        template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, EncodeCrossCrate::No
+        template!(List: &[r#"cfg = "...", module = "...""#]), DuplicatesOk, EncodeCrossCrate::No
     ),
     rustc_attr!(
         TEST, rustc_expected_cgu_reuse, Normal,
-        template!(List: r#"cfg = "...", module = "...", kind = "...""#), DuplicatesOk,
+        template!(List: &[r#"cfg = "...", module = "...", kind = "...""#]), DuplicatesOk,
         EncodeCrossCrate::No
     ),
     rustc_attr!(
@@ -1225,11 +1454,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         WarnFollowing, EncodeCrossCrate::No
     ),
     rustc_attr!(
-        TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."),
+        TEST, rustc_mir, Normal, template!(List: &["arg1, arg2, ..."]),
         DuplicatesOk, EncodeCrossCrate::Yes
     ),
     gated!(
-        custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#),
+        custom_mir, Normal, template!(List: &[r#"dialect = "...", phase = "...""#]),
         ErrorFollowing, EncodeCrossCrate::No,
         "the `#[custom_mir]` attribute is just used for the Rust test suite",
     ),
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
index 5f419315467..e02edf5fe24 100644
--- a/compiler/rustc_hir/src/attrs/data_structures.rs
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -249,6 +249,9 @@ pub enum AttributeKind {
     /// Represents `#[rustc_allow_incoherent_impl]`.
     AllowIncoherentImpl(Span),
 
+    /// Represents `#[allow_internal_unsafe]`.
+    AllowInternalUnsafe(Span),
+
     /// Represents `#[allow_internal_unstable]`.
     AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),
 
diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
index e3a7f0b97a8..7ce624dcc55 100644
--- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
+++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
@@ -16,6 +16,7 @@ impl AttributeKind {
             Align { .. } => No,
             AllowConstFnUnstable(..) => No,
             AllowIncoherentImpl(..) => No,
+            AllowInternalUnsafe(..) => Yes,
             AllowInternalUnstable(..) => Yes,
             AsPtr(..) => Yes,
             AutomaticallyDerived(..) => Yes,
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 34db6f92d92..b27c223527e 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1310,6 +1310,7 @@ impl AttributeExt for Attribute {
             Attribute::Parsed(AttributeKind::Ignore { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::ShouldPanic { span, .. }) => *span,
             Attribute::Parsed(AttributeKind::AutomaticallyDerived(span)) => *span,
+            Attribute::Parsed(AttributeKind::AllowInternalUnsafe(span)) => *span,
             a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
         }
     }
@@ -4194,7 +4195,7 @@ impl<'hir> Item<'hir> {
         expect_trait_alias, (Ident, &'hir Generics<'hir>, GenericBounds<'hir>),
             ItemKind::TraitAlias(ident, generics, bounds), (*ident, generics, bounds);
 
-        expect_impl, &'hir Impl<'hir>, ItemKind::Impl(imp), imp;
+        expect_impl, &Impl<'hir>, ItemKind::Impl(imp), imp;
     }
 }
 
@@ -4372,7 +4373,7 @@ pub enum ItemKind<'hir> {
     TraitAlias(Ident, &'hir Generics<'hir>, GenericBounds<'hir>),
 
     /// An implementation, e.g., `impl<A> Trait for Foo { .. }`.
-    Impl(&'hir Impl<'hir>),
+    Impl(Impl<'hir>),
 }
 
 /// Represents an impl block declaration.
@@ -4381,6 +4382,14 @@ pub enum ItemKind<'hir> {
 /// Refer to [`ImplItem`] for an associated item within an impl block.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct Impl<'hir> {
+    pub generics: &'hir Generics<'hir>,
+    pub of_trait: Option<&'hir TraitImplHeader<'hir>>,
+    pub self_ty: &'hir Ty<'hir>,
+    pub items: &'hir [ImplItemId],
+}
+
+#[derive(Debug, Clone, Copy, HashStable_Generic)]
+pub struct TraitImplHeader<'hir> {
     pub constness: Constness,
     pub safety: Safety,
     pub polarity: ImplPolarity,
@@ -4388,13 +4397,7 @@ pub struct Impl<'hir> {
     // We do not put a `Span` in `Defaultness` because it breaks foreign crate metadata
     // decoding as `Span`s cannot be decoded when a `Session` is not available.
     pub defaultness_span: Option<Span>,
-    pub generics: &'hir Generics<'hir>,
-
-    /// The trait being implemented, if any.
-    pub of_trait: Option<TraitRef<'hir>>,
-
-    pub self_ty: &'hir Ty<'hir>,
-    pub items: &'hir [ImplItemId],
+    pub trait_ref: TraitRef<'hir>,
 }
 
 impl ItemKind<'_> {
@@ -4756,8 +4759,8 @@ impl<'hir> Node<'hir> {
     /// Get a `hir::Impl` if the node is an impl block for the given `trait_def_id`.
     pub fn impl_block_of_trait(self, trait_def_id: DefId) -> Option<&'hir Impl<'hir>> {
         if let Node::Item(Item { kind: ItemKind::Impl(impl_block), .. }) = self
-            && let Some(trait_ref) = impl_block.of_trait
-            && let Some(trait_id) = trait_ref.trait_def_id()
+            && let Some(of_trait) = impl_block.of_trait
+            && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
             && trait_id == trait_def_id
         {
             Some(impl_block)
@@ -4952,7 +4955,7 @@ mod size_asserts {
     static_assert_size!(GenericArg<'_>, 16);
     static_assert_size!(GenericBound<'_>, 64);
     static_assert_size!(Generics<'_>, 56);
-    static_assert_size!(Impl<'_>, 80);
+    static_assert_size!(Impl<'_>, 40);
     static_assert_size!(ImplItem<'_>, 96);
     static_assert_size!(ImplItemKind<'_>, 40);
     static_assert_size!(Item<'_>, 88);
@@ -4967,6 +4970,7 @@ mod size_asserts {
     static_assert_size!(Res, 12);
     static_assert_size!(Stmt<'_>, 32);
     static_assert_size!(StmtKind<'_>, 16);
+    static_assert_size!(TraitImplHeader<'_>, 48);
     static_assert_size!(TraitItem<'_>, 88);
     static_assert_size!(TraitItemKind<'_>, 48);
     static_assert_size!(Ty<'_>, 48);
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 23fa466859a..9b2f8ae75fa 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -590,21 +590,21 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
             try_visit!(visitor.visit_generics(generics));
             try_visit!(visitor.visit_enum_def(enum_definition));
         }
-        ItemKind::Impl(Impl {
-            constness: _,
-            safety: _,
-            defaultness: _,
-            polarity: _,
-            defaultness_span: _,
-            generics,
-            of_trait,
-            self_ty,
-            items,
-        }) => {
+        ItemKind::Impl(Impl { generics, of_trait, self_ty, items }) => {
             try_visit!(visitor.visit_generics(generics));
-            visit_opt!(visitor, visit_trait_ref, of_trait);
+            if let Some(TraitImplHeader {
+                constness: _,
+                safety: _,
+                polarity: _,
+                defaultness: _,
+                defaultness_span: _,
+                trait_ref,
+            }) = of_trait
+            {
+                try_visit!(visitor.visit_trait_ref(trait_ref));
+            }
             try_visit!(visitor.visit_ty_unambig(self_ty));
-            walk_list!(visitor, visit_impl_item_ref, *items);
+            walk_list!(visitor, visit_impl_item_ref, items);
         }
         ItemKind::Struct(ident, ref generics, ref struct_definition)
         | ItemKind::Union(ident, ref generics, ref struct_definition) => {
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index a62efed13bc..c642435b989 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -244,48 +244,48 @@ pub(super) fn check_item<'tcx>(
         //
         // won't be allowed unless there's an *explicit* implementation of `Send`
         // for `T`
-        hir::ItemKind::Impl(impl_) => {
-            let header = tcx.impl_trait_header(def_id);
-            let is_auto = header
-                .is_some_and(|header| tcx.trait_is_auto(header.trait_ref.skip_binder().def_id));
-
+        hir::ItemKind::Impl(ref impl_) => {
             crate::impl_wf_check::check_impl_wf(tcx, def_id)?;
             let mut res = Ok(());
-            if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) {
-                let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span);
-                res = Err(tcx
-                    .dcx()
-                    .struct_span_err(sp, "impls of auto traits cannot be default")
-                    .with_span_labels(impl_.defaultness_span, "default because of this")
-                    .with_span_label(sp, "auto trait")
-                    .emit());
-            }
-            // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span.
-            match header.map(|h| h.polarity) {
-                // `None` means this is an inherent impl
-                Some(ty::ImplPolarity::Positive) | None => {
-                    res = res.and(check_impl(tcx, item, impl_.self_ty, &impl_.of_trait));
-                }
-                Some(ty::ImplPolarity::Negative) => {
-                    let ast::ImplPolarity::Negative(span) = impl_.polarity else {
-                        bug!("impl_polarity query disagrees with impl's polarity in HIR");
-                    };
-                    // FIXME(#27579): what amount of WF checking do we need for neg impls?
-                    if let hir::Defaultness::Default { .. } = impl_.defaultness {
-                        let mut spans = vec![span];
-                        spans.extend(impl_.defaultness_span);
-                        res = Err(struct_span_code_err!(
-                            tcx.dcx(),
-                            spans,
-                            E0750,
-                            "negative impls cannot be default impls"
-                        )
+            if let Some(of_trait) = impl_.of_trait {
+                let header = tcx.impl_trait_header(def_id).unwrap();
+                let is_auto = tcx.trait_is_auto(header.trait_ref.skip_binder().def_id);
+                if let (hir::Defaultness::Default { .. }, true) = (of_trait.defaultness, is_auto) {
+                    let sp = of_trait.trait_ref.path.span;
+                    res = Err(tcx
+                        .dcx()
+                        .struct_span_err(sp, "impls of auto traits cannot be default")
+                        .with_span_labels(of_trait.defaultness_span, "default because of this")
+                        .with_span_label(sp, "auto trait")
                         .emit());
-                    }
                 }
-                Some(ty::ImplPolarity::Reservation) => {
-                    // FIXME: what amount of WF checking do we need for reservation impls?
+                match header.polarity {
+                    ty::ImplPolarity::Positive => {
+                        res = res.and(check_impl(tcx, item, impl_));
+                    }
+                    ty::ImplPolarity::Negative => {
+                        let ast::ImplPolarity::Negative(span) = of_trait.polarity else {
+                            bug!("impl_polarity query disagrees with impl's polarity in HIR");
+                        };
+                        // FIXME(#27579): what amount of WF checking do we need for neg impls?
+                        if let hir::Defaultness::Default { .. } = of_trait.defaultness {
+                            let mut spans = vec![span];
+                            spans.extend(of_trait.defaultness_span);
+                            res = Err(struct_span_code_err!(
+                                tcx.dcx(),
+                                spans,
+                                E0750,
+                                "negative impls cannot be default impls"
+                            )
+                            .emit());
+                        }
+                    }
+                    ty::ImplPolarity::Reservation => {
+                        // FIXME: what amount of WF checking do we need for reservation impls?
+                    }
                 }
+            } else {
+                res = res.and(check_impl(tcx, item, impl_));
             }
             res
         }
@@ -1258,16 +1258,15 @@ pub(crate) fn check_const_item(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<()
     })
 }
 
-#[instrument(level = "debug", skip(tcx, hir_self_ty, hir_trait_ref))]
+#[instrument(level = "debug", skip(tcx, impl_))]
 fn check_impl<'tcx>(
     tcx: TyCtxt<'tcx>,
     item: &'tcx hir::Item<'tcx>,
-    hir_self_ty: &hir::Ty<'_>,
-    hir_trait_ref: &Option<hir::TraitRef<'_>>,
+    impl_: &hir::Impl<'_>,
 ) -> Result<(), ErrorGuaranteed> {
     enter_wf_checking_ctxt(tcx, item.owner_id.def_id, |wfcx| {
-        match hir_trait_ref {
-            Some(hir_trait_ref) => {
+        match impl_.of_trait {
+            Some(of_trait) => {
                 // `#[rustc_reservation_impl]` impls are not real impls and
                 // therefore don't need to be WF (the trait's `Self: Trait` predicate
                 // won't hold).
@@ -1275,7 +1274,7 @@ fn check_impl<'tcx>(
                 // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
                 // other `Foo` impls are incoherent.
                 tcx.ensure_ok().coherent_trait(trait_ref.def_id)?;
-                let trait_span = hir_trait_ref.path.span;
+                let trait_span = of_trait.trait_ref.path.span;
                 let trait_ref = wfcx.deeply_normalize(
                     trait_span,
                     Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
@@ -1299,12 +1298,12 @@ fn check_impl<'tcx>(
                     if let Some(pred) = obligation.predicate.as_trait_clause()
                         && pred.skip_binder().self_ty() == trait_ref.self_ty()
                     {
-                        obligation.cause.span = hir_self_ty.span;
+                        obligation.cause.span = impl_.self_ty.span;
                     }
                     if let Some(pred) = obligation.predicate.as_projection_clause()
                         && pred.skip_binder().self_ty() == trait_ref.self_ty()
                     {
-                        obligation.cause.span = hir_self_ty.span;
+                        obligation.cause.span = impl_.self_ty.span;
                     }
                 }
 
@@ -1321,7 +1320,7 @@ fn check_impl<'tcx>(
                         wfcx.register_obligation(Obligation::new(
                             tcx,
                             ObligationCause::new(
-                                hir_self_ty.span,
+                                impl_.self_ty.span,
                                 wfcx.body_def_id,
                                 ObligationCauseCode::WellFormed(None),
                             ),
@@ -1342,7 +1341,7 @@ fn check_impl<'tcx>(
                     self_ty,
                 );
                 wfcx.register_wf_obligation(
-                    hir_self_ty.span,
+                    impl_.self_ty.span,
                     Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
                     self_ty.into(),
                 );
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 27948f50a4a..32b175611ce 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -531,8 +531,10 @@ pub(crate) fn coerce_unsized_info<'tcx>(
                 }));
             } else if diff_fields.len() > 1 {
                 let item = tcx.hir_expect_item(impl_did);
-                let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind {
-                    t.path.span
+                let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) =
+                    &item.kind
+                {
+                    of_trait.trait_ref.path.span
                 } else {
                     tcx.def_span(impl_did)
                 };
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index c75fef9f716..f707196c816 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -384,7 +384,7 @@ fn emit_orphan_check_error<'tcx>(
         traits::OrphanCheckErr::NonLocalInputType(tys) => {
             let item = tcx.hir_expect_item(impl_def_id);
             let impl_ = item.expect_impl();
-            let hir_trait_ref = impl_.of_trait.as_ref().unwrap();
+            let of_trait = impl_.of_trait.unwrap();
 
             let span = tcx.def_span(impl_def_id);
             let mut diag = tcx.dcx().create_err(match trait_ref.self_ty().kind() {
@@ -401,7 +401,7 @@ fn emit_orphan_check_error<'tcx>(
                     impl_.self_ty.span
                 } else {
                     // Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
-                    hir_trait_ref.path.span
+                    of_trait.trait_ref.path.span
                 };
 
                 ty = tcx.erase_regions(ty);
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 8ccbfbbb3b4..b72e743f95b 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1295,18 +1295,22 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai
     let icx = ItemCtxt::new(tcx, def_id);
     let item = tcx.hir_expect_item(def_id);
     let impl_ = item.expect_impl();
-    impl_.of_trait.as_ref().map(|ast_trait_ref| {
+    let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
+    if is_rustc_reservation && impl_.of_trait.is_none() {
+        tcx.dcx().span_err(item.span, "reservation impls can't be inherent");
+    }
+    impl_.of_trait.map(|of_trait| {
         let selfty = tcx.type_of(def_id).instantiate_identity();
 
-        check_impl_constness(tcx, impl_.constness, ast_trait_ref);
+        check_impl_constness(tcx, of_trait.constness, &of_trait.trait_ref);
 
-        let trait_ref = icx.lowerer().lower_impl_trait_ref(ast_trait_ref, selfty);
+        let trait_ref = icx.lowerer().lower_impl_trait_ref(&of_trait.trait_ref, selfty);
 
         ty::ImplTraitHeader {
             trait_ref: ty::EarlyBinder::bind(trait_ref),
-            safety: impl_.safety,
-            polarity: polarity_of_impl(tcx, def_id, impl_, item.span),
-            constness: impl_.constness,
+            safety: of_trait.safety,
+            polarity: polarity_of_impl(tcx, of_trait, is_rustc_reservation),
+            constness: of_trait.constness,
         }
     })
 }
@@ -1350,26 +1354,18 @@ fn check_impl_constness(
 
 fn polarity_of_impl(
     tcx: TyCtxt<'_>,
-    def_id: LocalDefId,
-    impl_: &hir::Impl<'_>,
-    span: Span,
+    of_trait: &hir::TraitImplHeader<'_>,
+    is_rustc_reservation: bool,
 ) -> ty::ImplPolarity {
-    let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
-    match &impl_ {
-        hir::Impl { polarity: hir::ImplPolarity::Negative(span), of_trait, .. } => {
+    match of_trait.polarity {
+        hir::ImplPolarity::Negative(span) => {
             if is_rustc_reservation {
-                let span = span.to(of_trait.as_ref().map_or(*span, |t| t.path.span));
+                let span = span.to(of_trait.trait_ref.path.span);
                 tcx.dcx().span_err(span, "reservation impls can't be negative");
             }
             ty::ImplPolarity::Negative
         }
-        hir::Impl { polarity: hir::ImplPolarity::Positive, of_trait: None, .. } => {
-            if is_rustc_reservation {
-                tcx.dcx().span_err(span, "reservation impls can't be inherent");
-            }
-            ty::ImplPolarity::Positive
-        }
-        hir::Impl { polarity: hir::ImplPolarity::Positive, of_trait: Some(_), .. } => {
+        hir::ImplPolarity::Positive => {
             if is_rustc_reservation {
                 ty::ImplPolarity::Reservation
             } else {
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 8dd13da4fa7..b59dc4bd132 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -158,7 +158,9 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     if let Node::Item(item) = node {
         match item.kind {
             ItemKind::Impl(impl_) => {
-                if impl_.defaultness.is_default() {
+                if let Some(of_trait) = impl_.of_trait
+                    && of_trait.defaultness.is_default()
+                {
                     is_default_impl_trait = tcx
                         .impl_trait_ref(def_id)
                         .map(|t| ty::Binder::dummy(t.instantiate_identity()));
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index eb3492f5de6..8133f9f6823 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -604,13 +604,10 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
 
     #[instrument(level = "debug", skip(self))]
     fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
-        match &item.kind {
-            hir::ItemKind::Impl(hir::Impl { of_trait, .. }) => {
-                if let Some(of_trait) = of_trait {
-                    self.record_late_bound_vars(of_trait.hir_ref_id, Vec::default());
-                }
-            }
-            _ => {}
+        if let hir::ItemKind::Impl(impl_) = item.kind
+            && let Some(of_trait) = impl_.of_trait
+        {
+            self.record_late_bound_vars(of_trait.trait_ref.hir_ref_id, Vec::default());
         }
         match item.kind {
             hir::ItemKind::Fn { generics, .. } => {
@@ -636,7 +633,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
             | hir::ItemKind::Union(_, generics, _)
             | hir::ItemKind::Trait(_, _, _, _, generics, ..)
             | hir::ItemKind::TraitAlias(_, generics, ..)
-            | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => {
+            | hir::ItemKind::Impl(hir::Impl { generics, .. }) => {
                 // These kinds of items have only early-bound lifetime parameters.
                 self.visit_early(item.hir_id(), generics, |this| intravisit::walk_item(this, item));
             }
@@ -2106,7 +2103,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                     // If we have a self type alias (in an impl), try to resolve an
                     // associated item from one of the supertraits of the impl's trait.
                     Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. } => {
-                        let hir::ItemKind::Impl(hir::Impl { of_trait: Some(trait_ref), .. }) = self
+                        let hir::ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), .. }) = self
                             .tcx
                             .hir_node_by_def_id(impl_def_id.expect_local())
                             .expect_item()
@@ -2114,7 +2111,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                         else {
                             return;
                         };
-                        let Some(trait_def_id) = trait_ref.trait_def_id() else {
+                        let Some(trait_def_id) = of_trait.trait_ref.trait_def_id() else {
                             return;
                         };
                         let Some((bound_vars, assoc_item)) = BoundVarContext::supertrait_hrtb_vars(
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 22fb02714dd..62125c99d80 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -251,7 +251,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
                         .emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: () });
                     Ty::new_error(tcx, guar)
                 }
-                _ => icx.lower_ty(*self_ty),
+                _ => icx.lower_ty(self_ty),
             },
             ItemKind::Fn { .. } => {
                 let args = ty::GenericArgs::identity_for_item(tcx, def_id);
diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
index 835f8e8cdae..8a9f9130fea 100644
--- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
@@ -147,7 +147,11 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
             let hir::Node::Item(hir::Item {
                 kind:
                     hir::ItemKind::Impl(hir::Impl {
-                        of_trait: Some(hir::TraitRef { hir_ref_id: id_in_of_trait, .. }),
+                        of_trait:
+                            Some(hir::TraitImplHeader {
+                                trait_ref: hir::TraitRef { hir_ref_id: id_in_of_trait, .. },
+                                ..
+                            }),
                         ..
                     }),
                 ..
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
index 646ff3ca08d..56998b5b53c 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -200,7 +200,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }) = tcx.hir_node_by_def_id(parent_id)
             && self_ty.hir_id == impl_self_ty.hir_id
         {
-            let Some(of_trait_ref) = of_trait else {
+            let Some(of_trait) = of_trait else {
                 diag.span_suggestion_verbose(
                     impl_self_ty.span.shrink_to_hi(),
                     "you might have intended to implement this trait for a given type",
@@ -209,10 +209,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 );
                 return;
             };
-            if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) {
+            if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) {
                 return;
             }
-            let of_trait_span = of_trait_ref.path.span;
+            let of_trait_span = of_trait.trait_ref.path.span;
             // make sure that we are not calling unwrap to abort during the compilation
             let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else {
                 return;
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 1675aecd2b8..c7b984d9b25 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -2732,7 +2732,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         };
         let i = tcx.parent_hir_node(fn_hir_id).expect_item().expect_impl();
 
-        let trait_ref = self.lower_impl_trait_ref(i.of_trait.as_ref()?, self.lower_ty(i.self_ty));
+        let trait_ref = self.lower_impl_trait_ref(&i.of_trait?.trait_ref, self.lower_ty(i.self_ty));
 
         let assoc = tcx.associated_items(trait_ref.def_id).find_by_ident_and_kind(
             tcx,
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index 3fddaee8cef..d8578970adc 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -154,8 +154,9 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>(
                 hir::ItemKind::TyAlias(_, _, ty)
                 | hir::ItemKind::Static(_, _, ty, _)
                 | hir::ItemKind::Const(_, _, ty, _) => vec![ty],
-                hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
-                    Some(t) => t
+                hir::ItemKind::Impl(impl_) => match impl_.of_trait {
+                    Some(of_trait) => of_trait
+                        .trait_ref
                         .path
                         .segments
                         .last()
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 235eec96d74..be5859b57c5 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -690,39 +690,44 @@ impl<'a> State<'a> {
                 let (cb, ib) = self.head("union");
                 self.print_struct(ident.name, generics, struct_def, item.span, true, cb, ib);
             }
-            hir::ItemKind::Impl(&hir::Impl {
-                constness,
-                safety,
-                polarity,
-                defaultness,
-                defaultness_span: _,
-                generics,
-                ref of_trait,
-                self_ty,
-                items,
-            }) => {
+            hir::ItemKind::Impl(hir::Impl { generics, of_trait, self_ty, items }) => {
                 let (cb, ib) = self.head("");
-                self.print_defaultness(defaultness);
-                self.print_safety(safety);
-                self.word_nbsp("impl");
 
-                if let hir::Constness::Const = constness {
-                    self.word_nbsp("const");
-                }
+                let impl_generics = |this: &mut Self| {
+                    this.word_nbsp("impl");
+                    if !generics.params.is_empty() {
+                        this.print_generic_params(generics.params);
+                        this.space();
+                    }
+                };
 
-                if !generics.params.is_empty() {
-                    self.print_generic_params(generics.params);
-                    self.space();
-                }
+                match of_trait {
+                    None => impl_generics(self),
+                    Some(&hir::TraitImplHeader {
+                        constness,
+                        safety,
+                        polarity,
+                        defaultness,
+                        defaultness_span: _,
+                        ref trait_ref,
+                    }) => {
+                        self.print_defaultness(defaultness);
+                        self.print_safety(safety);
+
+                        impl_generics(self);
+
+                        if let hir::Constness::Const = constness {
+                            self.word_nbsp("const");
+                        }
 
-                if let hir::ImplPolarity::Negative(_) = polarity {
-                    self.word("!");
-                }
+                        if let hir::ImplPolarity::Negative(_) = polarity {
+                            self.word("!");
+                        }
 
-                if let Some(t) = of_trait {
-                    self.print_trait_ref(t);
-                    self.space();
-                    self.word_space("for");
+                        self.print_trait_ref(trait_ref);
+                        self.space();
+                        self.word_space("for");
+                    }
                 }
 
                 self.print_type(self_ty);
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 2345cdab208..6013430e1ff 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -936,7 +936,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Node::ImplItem(item) => {
                 // If it doesn't impl a trait, we can add a return type
                 let Node::Item(&hir::Item {
-                    kind: hir::ItemKind::Impl(&hir::Impl { of_trait, .. }),
+                    kind: hir::ItemKind::Impl(hir::Impl { of_trait, .. }),
                     ..
                 }) = self.tcx.parent_hir_node(item.hir_id())
                 else {
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index f7430f7af4e..824d592fa6c 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -1103,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         self_ty.span.ctxt().outer_expn_data().kind,
                         ExpnKind::Macro(MacroKind::Derive, _)
                     ) || matches!(
-                        of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind),
+                        of_trait.map(|t| t.trait_ref.path.span.ctxt().outer_expn_data().kind),
                         Some(ExpnKind::Macro(MacroKind::Derive, _))
                     ) =>
                     {
@@ -1165,13 +1165,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             entry.0.insert(cause_span);
                             entry.1.insert((cause_span, "unsatisfied trait bound introduced here"));
                         } else {
-                            if let Some(trait_ref) = of_trait {
-                                entry.0.insert(trait_ref.path.span);
+                            if let Some(of_trait) = of_trait {
+                                entry.0.insert(of_trait.trait_ref.path.span);
                             }
                             entry.0.insert(self_ty.span);
                         };
-                        if let Some(trait_ref) = of_trait {
-                            entry.1.insert((trait_ref.path.span, ""));
+                        if let Some(of_trait) = of_trait {
+                            entry.1.insert((of_trait.trait_ref.path.span, ""));
                         }
                         entry.1.insert((self_ty.span, ""));
                     }
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 4d0c0c94a81..776d8d35e05 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -349,6 +349,7 @@ lint_ill_formed_attribute_input = {$num_suggestions ->
         [1] attribute must be of the form {$suggestions}
         *[other] valid forms for the attribute are {$suggestions}
     }
+    .note = for more information, visit <{$docs}>
 
 lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than possibly intended in edition 2024
     .note = specifically, {$num_captured ->
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index c893b723375..8006cfcf30f 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -21,6 +21,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast::visit::{FnCtxt, FnKind};
 use rustc_ast::{self as ast, *};
 use rustc_ast_pretty::pprust::expr_to_string;
+use rustc_attr_parsing::AttributeParser;
 use rustc_errors::{Applicability, LintDiagnostic};
 use rustc_feature::GateIssue;
 use rustc_hir as hir;
@@ -248,12 +249,6 @@ impl UnsafeCode {
 }
 
 impl EarlyLintPass for UnsafeCode {
-    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
-        if attr.has_name(sym::allow_internal_unsafe) {
-            self.report_unsafe(cx, attr.span, BuiltinUnsafe::AllowInternalUnsafe);
-        }
-    }
-
     #[inline]
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
         if let ast::ExprKind::Block(ref blk, _) = e.kind {
@@ -270,7 +265,10 @@ impl EarlyLintPass for UnsafeCode {
                 self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeTrait);
             }
 
-            ast::ItemKind::Impl(box ast::Impl { safety: ast::Safety::Unsafe(_), .. }) => {
+            ast::ItemKind::Impl(ast::Impl {
+                of_trait: Some(box ast::TraitImplHeader { safety: ast::Safety::Unsafe(_), .. }),
+                ..
+            }) => {
                 self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeImpl);
             }
 
@@ -312,6 +310,19 @@ impl EarlyLintPass for UnsafeCode {
                 }
             }
 
+            ast::ItemKind::MacroDef(..) => {
+                if let Some(attr) = AttributeParser::parse_limited(
+                    cx.builder.sess(),
+                    &it.attrs,
+                    sym::allow_internal_unsafe,
+                    it.span,
+                    DUMMY_NODE_ID,
+                    Some(cx.builder.features()),
+                ) {
+                    self.report_unsafe(cx, attr.span(), BuiltinUnsafe::AllowInternalUnsafe);
+                }
+            }
+
             _ => {}
         }
     }
diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
index dd16117db1c..943fcc0801b 100644
--- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
+++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
@@ -61,8 +61,8 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait {
         // `Deref` is being implemented for `t`
         if let hir::ItemKind::Impl(impl_) = item.kind
             // the trait is a `Deref` implementation
-            && let Some(trait_) = &impl_.of_trait
-            && let Some(did) = trait_.trait_def_id()
+            && let Some(of_trait) = &impl_.of_trait
+            && let Some(did) = of_trait.trait_ref.trait_def_id()
             && tcx.is_lang_item(did, LangItem::Deref)
             // the self type is `dyn t_principal`
             && let self_ty = tcx.type_of(item.owner_id).instantiate_identity()
diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs
index f0fbf5bc81e..1e4bc79ce70 100644
--- a/compiler/rustc_lint/src/early/diagnostics.rs
+++ b/compiler/rustc_lint/src/early/diagnostics.rs
@@ -447,12 +447,14 @@ pub fn decorate_builtin_lint(
         BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
             lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
         }
-        BuiltinLintDiag::IllFormedAttributeInput { suggestions } => {
+        BuiltinLintDiag::IllFormedAttributeInput { suggestions, docs } => {
             lints::IllFormedAttributeInput {
                 num_suggestions: suggestions.len(),
                 suggestions: DiagArgValue::StrListSepByAnd(
                     suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
                 ),
+                has_docs: docs.is_some(),
+                docs: docs.unwrap_or(""),
             }
             .decorate_lint(diag)
         }
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 7dafcc199a3..016ff17f5d7 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -411,11 +411,11 @@ declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);
 
 impl EarlyLintPass for LintPassImpl {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind
-            && let Some(last) = lint_pass.path.segments.last()
+        if let ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) = &item.kind
+            && let Some(last) = of_trait.trait_ref.path.segments.last()
             && last.ident.name == sym::LintPass
         {
-            let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
+            let expn_data = of_trait.trait_ref.path.span.ctxt().outer_expn_data();
             let call_site = expn_data.call_site;
             if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
                 && call_site.ctxt().outer_expn_data().kind
@@ -423,7 +423,7 @@ impl EarlyLintPass for LintPassImpl {
             {
                 cx.emit_span_lint(
                     LINT_PASS_IMPL_WITHOUT_MACRO,
-                    lint_pass.path.span,
+                    of_trait.trait_ref.path.span,
                     LintPassByHand,
                 );
             }
@@ -582,8 +582,8 @@ impl Diagnostics {
         for (_hir_id, parent) in cx.tcx.hir_parent_iter(current_id) {
             debug!(?parent);
             if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) = parent
-                && let hir::Impl { of_trait: Some(of_trait), .. } = impl_
-                && let Some(def_id) = of_trait.trait_def_id()
+                && let Some(of_trait) = impl_.of_trait
+                && let Some(def_id) = of_trait.trait_ref.trait_def_id()
                 && let Some(name) = cx.tcx.get_diagnostic_name(def_id)
                 && matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
             {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 73e69a1791a..ba0112c8ac6 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -2685,6 +2685,9 @@ pub(crate) struct UnusedCrateDependency {
 pub(crate) struct IllFormedAttributeInput {
     pub num_suggestions: usize,
     pub suggestions: DiagArgValue,
+    #[note]
+    pub has_docs: bool,
+    pub docs: &'static str,
 }
 
 #[derive(LintDiagnostic)]
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index b877f909fc0..2dd3425e66c 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -129,8 +129,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                 // of the `impl` definition
                 let mut collector = PathCollector { paths: Vec::new() };
                 collector.visit_ty_unambig(&impl_.self_ty);
-                if let Some(of_trait) = &impl_.of_trait {
-                    collector.visit_trait_ref(of_trait);
+                if let Some(of_trait) = impl_.of_trait {
+                    collector.visit_trait_ref(&of_trait.trait_ref);
                 }
 
                 // 1.5. Remove any path that doesn't resolve to a `DefId` or if it resolve to a
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 7e5f43ba77f..8fafaa33d0c 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -187,7 +187,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
 
             // N.B. This check is only for inherent associated types, so that we don't lint against
             // trait impls where we should have warned for the trait definition already.
-            ast::ItemKind::Impl(box ast::Impl { of_trait: None, items, .. }) => {
+            ast::ItemKind::Impl(ast::Impl { of_trait: None, items, .. }) => {
                 for it in items {
                     // FIXME: this doesn't respect `#[allow(..)]` on the item itself.
                     if let ast::AssocItemKind::Type(alias) = &it.kind {
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index fe068d96b74..dc5ea3922f1 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -793,6 +793,7 @@ pub enum BuiltinLintDiag {
     },
     IllFormedAttributeInput {
         suggestions: Vec<String>,
+        docs: Option<&'static str>,
     },
     InnerAttributeUnstable {
         is_macro: bool,
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index aaf1b6c05bf..3a21eea3d0a 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -869,6 +869,11 @@ parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto`
 parse_trait_alias_cannot_be_const = trait aliases cannot be `const`
 parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe`
 
+parse_trait_impl_modifier_in_inherent_impl = inherent impls cannot be {$modifier_name}
+    .because = {$modifier_name} because of this
+    .type = inherent impl for this type
+    .note = only trait implementations may be annotated with `{$modifier}`
+
 parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before
     .suggestion = move `{$kw}` before the `for<...>`
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index ddb2c545c78..a07d0606fd0 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -71,6 +71,20 @@ pub(crate) struct BadQPathStage2 {
     pub wrap: WrapType,
 }
 
+#[derive(Diagnostic)]
+#[diag(parse_trait_impl_modifier_in_inherent_impl)]
+#[note]
+pub(crate) struct TraitImplModifierInInherentImpl {
+    #[primary_span]
+    pub span: Span,
+    pub modifier: &'static str,
+    pub modifier_name: &'static str,
+    #[label(parse_because)]
+    pub modifier_span: Span,
+    #[label(parse_type)]
+    pub self_ty: Span,
+}
+
 #[derive(Subdiagnostic)]
 #[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
 pub(crate) struct WrapType {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 14a90e74049..607adaf0829 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -663,20 +663,44 @@ impl<'a> Parser<'a> {
                 };
                 let trait_ref = TraitRef { path, ref_id: ty_first.id };
 
-                (Some(trait_ref), ty_second)
+                let of_trait = Some(Box::new(TraitImplHeader {
+                    defaultness,
+                    safety,
+                    constness,
+                    polarity,
+                    trait_ref,
+                }));
+                (of_trait, ty_second)
+            }
+            None => {
+                let self_ty = ty_first;
+                let error = |modifier, modifier_name, modifier_span| {
+                    self.dcx().create_err(errors::TraitImplModifierInInherentImpl {
+                        span: self_ty.span,
+                        modifier,
+                        modifier_name,
+                        modifier_span,
+                        self_ty: self_ty.span,
+                    })
+                };
+
+                if let Safety::Unsafe(span) = safety {
+                    error("unsafe", "unsafe", span).with_code(E0197).emit();
+                }
+                if let ImplPolarity::Negative(span) = polarity {
+                    error("!", "negative", span).emit();
+                }
+                if let Defaultness::Default(def_span) = defaultness {
+                    error("default", "default", def_span).emit();
+                }
+                if let Const::Yes(span) = constness {
+                    error("const", "const", span).emit();
+                }
+                (None, self_ty)
             }
-            None => (None, ty_first), // impl Type
         };
-        Ok(ItemKind::Impl(Box::new(Impl {
-            safety,
-            polarity,
-            defaultness,
-            constness,
-            generics,
-            of_trait,
-            self_ty,
-            items: impl_items,
-        })))
+
+        Ok(ItemKind::Impl(Impl { generics, of_trait, self_ty, items: impl_items }))
     }
 
     fn parse_item_delegation(&mut self) -> PResult<'a, ItemKind> {
@@ -1364,10 +1388,10 @@ impl<'a> Parser<'a> {
         };
 
         match &mut item_kind {
-            ItemKind::Impl(box Impl { of_trait: Some(trai), constness, .. }) => {
-                *constness = Const::Yes(const_span);
+            ItemKind::Impl(Impl { of_trait: Some(of_trait), .. }) => {
+                of_trait.constness = Const::Yes(const_span);
 
-                let before_trait = trai.path.span.shrink_to_lo();
+                let before_trait = of_trait.trait_ref.path.span.shrink_to_lo();
                 let const_up_to_impl = const_span.with_hi(impl_span.lo());
                 err.with_multipart_suggestion(
                     "you might have meant to write a const trait impl",
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index a7f8d3b9139..68ef6d6f32c 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -298,35 +298,42 @@ fn emit_malformed_attribute(
         suggestions.push(format!("#{inner}[{name}]"));
     }
     if let Some(descr) = template.list {
-        suggestions.push(format!("#{inner}[{name}({descr})]"));
+        for descr in descr {
+            suggestions.push(format!("#{inner}[{name}({descr})]"));
+        }
     }
     suggestions.extend(template.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
     if let Some(descr) = template.name_value_str {
-        suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
+        for descr in descr {
+            suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
+        }
     }
     if should_warn(name) {
         psess.buffer_lint(
             ILL_FORMED_ATTRIBUTE_INPUT,
             span,
             ast::CRATE_NODE_ID,
-            BuiltinLintDiag::IllFormedAttributeInput { suggestions: suggestions.clone() },
+            BuiltinLintDiag::IllFormedAttributeInput {
+                suggestions: suggestions.clone(),
+                docs: template.docs,
+            },
         );
     } else {
         suggestions.sort();
-        psess
-            .dcx()
-            .struct_span_err(span, error_msg)
-            .with_span_suggestions(
-                span,
-                if suggestions.len() == 1 {
-                    "must be of the form"
-                } else {
-                    "the following are the possible correct uses"
-                },
-                suggestions,
-                Applicability::HasPlaceholders,
-            )
-            .emit();
+        let mut err = psess.dcx().struct_span_err(span, error_msg).with_span_suggestions(
+            span,
+            if suggestions.len() == 1 {
+                "must be of the form"
+            } else {
+                "the following are the possible correct uses"
+            },
+            suggestions,
+            Applicability::HasPlaceholders,
+        );
+        if let Some(link) = template.docs {
+            err.note(format!("for more information, visit <{link}>"));
+        }
+        err.emit();
     }
 }
 
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 6a28fe2617e..b5031f5d02e 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -29,7 +29,7 @@ passes_allow_incoherent_impl =
     `rustc_allow_incoherent_impl` attribute should be applied to impl items
     .label = the only currently supported targets are inherent methods
 
-passes_allow_internal_unstable =
+passes_macro_only_attribute =
     attribute should be applied to a macro
     .label = not a macro
 
@@ -669,8 +669,8 @@ passes_rustc_std_internal_symbol =
     .label = not a function or static
 
 passes_rustc_unstable_feature_bound =
-    attribute should be applied to `impl` or free function outside of any `impl` or trait
-    .label = not an `impl` or free function
+    attribute should be applied to `impl`, trait or free function
+    .label = not an `impl`, trait or free function
 
 passes_should_be_applied_to_fn =
     attribute should be applied to a function definition
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 10c532b436a..93b94a8ba14 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -207,6 +207,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::ConstContinue(attr_span)) => {
                     self.check_const_continue(hir_id, *attr_span, target)
                 }
+                Attribute::Parsed(AttributeKind::AllowInternalUnsafe(attr_span)) => {
+                    self.check_allow_internal_unsafe(hir_id, *attr_span, span, target, attrs)
+                }
                 Attribute::Parsed(AttributeKind::AllowInternalUnstable(_, first_span)) => {
                     self.check_allow_internal_unstable(hir_id, *first_span, span, target, attrs)
                 }
@@ -413,7 +416,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             // internal
                             | sym::prelude_import
                             | sym::panic_handler
-                            | sym::allow_internal_unsafe
                             | sym::lang
                             | sym::needs_allocator
                             | sym::default_lib_allocator
@@ -1155,8 +1157,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 let is_valid = doc_fake_variadic_is_allowed_self_ty(i.self_ty)
                     || if let Some(&[hir::GenericArg::Type(ty)]) = i
                         .of_trait
-                        .as_ref()
-                        .and_then(|trait_ref| trait_ref.path.segments.last())
+                        .and_then(|of_trait| of_trait.trait_ref.path.segments.last())
                         .map(|last_segment| last_segment.args().args)
                     {
                         matches!(&ty.kind, hir::TyKind::Tup([_]))
@@ -1646,8 +1647,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             && let parent_hir_id = self.tcx.parent_hir_id(hir_id)
             && let hir::Node::Item(item) = self.tcx.hir_node(parent_hir_id)
             && let hir::ItemKind::Impl(impl_) = item.kind
-            && let Some(trait_) = impl_.of_trait
-            && let Some(def_id) = trait_.trait_def_id()
+            && let Some(of_trait) = impl_.of_trait
+            && let Some(def_id) = of_trait.trait_ref.trait_def_id()
             && self.tcx.is_lang_item(def_id, hir::LangItem::Drop)
         {
             return;
@@ -2212,7 +2213,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
 
     /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
     /// (Allows proc_macro functions)
-    // FIXME(jdonszelmann): if possible, move to attr parsing
     fn check_allow_internal_unstable(
         &self,
         hir_id: HirId,
@@ -2221,6 +2221,42 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         target: Target,
         attrs: &[Attribute],
     ) {
+        self.check_macro_only_attr(
+            hir_id,
+            attr_span,
+            span,
+            target,
+            attrs,
+            "allow_internal_unstable",
+        )
+    }
+
+    /// Outputs an error for `#[allow_internal_unsafe]` which can only be applied to macros.
+    /// (Allows proc_macro functions)
+    fn check_allow_internal_unsafe(
+        &self,
+        hir_id: HirId,
+        attr_span: Span,
+        span: Span,
+        target: Target,
+        attrs: &[Attribute],
+    ) {
+        self.check_macro_only_attr(hir_id, attr_span, span, target, attrs, "allow_internal_unsafe")
+    }
+
+    /// Outputs an error for attributes that can only be applied to macros, such as
+    /// `#[allow_internal_unsafe]` and `#[allow_internal_unstable]`.
+    /// (Allows proc_macro functions)
+    // FIXME(jdonszelmann): if possible, move to attr parsing
+    fn check_macro_only_attr(
+        &self,
+        hir_id: HirId,
+        attr_span: Span,
+        span: Span,
+        target: Target,
+        attrs: &[Attribute],
+        attr_name: &str,
+    ) {
         match target {
             Target::Fn => {
                 for attr in attrs {
@@ -2238,18 +2274,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             // erroneously allowed it and some crates used it accidentally, to be compatible
             // with crates depending on them, we can't throw an error here.
             Target::Field | Target::Arm => {
-                self.inline_attr_str_error_without_macro_def(
-                    hir_id,
-                    attr_span,
-                    "allow_internal_unstable",
-                );
+                self.inline_attr_str_error_without_macro_def(hir_id, attr_span, attr_name);
                 return;
             }
             // otherwise continue out of the match
             _ => {}
         }
 
-        self.tcx.dcx().emit_err(errors::AllowInternalUnstable { attr_span, span });
+        self.tcx.dcx().emit_err(errors::MacroOnlyAttribute { attr_span, span });
     }
 
     /// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
@@ -2294,7 +2326,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         match target {
             // FIXME(staged_api): There's no reason we can't support more targets here. We're just
             // being conservative to begin with.
-            Target::Fn | Target::Impl { .. } => {}
+            Target::Fn | Target::Impl { .. } | Target::Trait => {}
             Target::ExternCrate
             | Target::Use
             | Target::Static
@@ -2309,7 +2341,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             | Target::Struct
             | Target::Field
             | Target::Union
-            | Target::Trait
             | Target::TraitAlias
             | Target::Expression
             | Target::Statement
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index fa9d0c7b1b7..d5d7cc5dc2e 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -172,7 +172,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         }
     }
 
-    #[allow(dead_code)] // FIXME(81658): should be used + lint reinstated after #83171 relands.
     fn handle_assign(&mut self, expr: &'tcx hir::Expr<'tcx>) {
         if self
             .typeck_results()
@@ -189,7 +188,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         }
     }
 
-    #[allow(dead_code)] // FIXME(81658): should be used + lint reinstated after #83171 relands.
     fn check_for_self_assign(&mut self, assign: &'tcx hir::Expr<'tcx>) {
         fn check_for_self_assign_helper<'tcx>(
             typeck_results: &'tcx ty::TypeckResults<'tcx>,
@@ -576,6 +574,10 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
             hir::ExprKind::OffsetOf(..) => {
                 self.handle_offset_of(expr);
             }
+            hir::ExprKind::Assign(ref lhs, ..) => {
+                self.handle_assign(lhs);
+                self.check_for_self_assign(expr);
+            }
             _ => (),
         }
 
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index c6ab6b0d601..10b30fbe8c9 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -643,8 +643,8 @@ pub(crate) struct UsedStatic {
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_allow_internal_unstable)]
-pub(crate) struct AllowInternalUnstable {
+#[diag(passes_macro_only_attribute)]
+pub(crate) struct MacroOnlyAttribute {
     #[primary_span]
     pub attr_span: Span,
     #[label]
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index e2f223325df..71650c6b9b9 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -590,9 +590,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
             // For implementations of traits, check the stability of each item
             // individually as it's possible to have a stable trait with unstable
             // items.
-            hir::ItemKind::Impl(hir::Impl {
-                of_trait: Some(t), self_ty, items, constness, ..
-            }) => {
+            hir::ItemKind::Impl(hir::Impl { of_trait: Some(of_trait), self_ty, items, .. }) => {
                 let features = self.tcx.features();
                 if features.staged_api() {
                     let attrs = self.tcx.hir_attrs(item.hir_id());
@@ -628,7 +626,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                     {
                         let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true };
                         c.visit_ty_unambig(self_ty);
-                        c.visit_trait_ref(t);
+                        c.visit_trait_ref(&of_trait.trait_ref);
 
                         // Skip the lint if the impl is marked as unstable using
                         // #[unstable_feature_bound(..)]
@@ -641,7 +639,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
 
                         // do not lint when the trait isn't resolved, since resolution error should
                         // be fixed first
-                        if t.path.res != Res::Err
+                        if of_trait.trait_ref.path.res != Res::Err
                             && c.fully_stable
                             && !unstable_feature_bound_in_effect
                         {
@@ -655,7 +653,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                     }
 
                     if features.const_trait_impl()
-                        && let hir::Constness::Const = constness
+                        && let hir::Constness::Const = of_trait.constness
                     {
                         let stable_or_implied_stable = match const_stab {
                             None => true,
@@ -671,7 +669,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                             Some(_) => false,
                         };
 
-                        if let Some(trait_id) = t.trait_def_id()
+                        if let Some(trait_id) = of_trait.trait_ref.trait_def_id()
                             && let Some(const_stab) = self.tcx.lookup_const_stability(trait_id)
                         {
                             // the const stability of a trait impl must match the const stability on the trait.
@@ -699,14 +697,18 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                     }
                 }
 
-                if let hir::Constness::Const = constness
-                    && let Some(def_id) = t.trait_def_id()
+                if let hir::Constness::Const = of_trait.constness
+                    && let Some(def_id) = of_trait.trait_ref.trait_def_id()
                 {
                     // FIXME(const_trait_impl): Improve the span here.
-                    self.tcx.check_const_stability(def_id, t.path.span, t.path.span);
+                    self.tcx.check_const_stability(
+                        def_id,
+                        of_trait.trait_ref.path.span,
+                        of_trait.trait_ref.path.span,
+                    );
                 }
 
-                for impl_item_ref in *items {
+                for impl_item_ref in items {
                     let impl_item = self.tcx.associated_item(impl_item_ref.owner_id);
 
                     if let Some(def_id) = impl_item.trait_item_def_id {
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 5d02c02b23c..1bddbd03cc3 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -1779,7 +1779,8 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
         if let DefKind::Impl { of_trait: true } = tcx.def_kind(def_id) {
             let trait_ref = tcx.impl_trait_ref(def_id).unwrap();
             let trait_ref = trait_ref.instantiate_identity();
-            visitor.span = tcx.hir_expect_item(def_id).expect_impl().of_trait.unwrap().path.span;
+            visitor.span =
+                tcx.hir_expect_item(def_id).expect_impl().of_trait.unwrap().trait_ref.path.span;
             let _ =
                 visitor.visit_def_id(trait_ref.def_id, "trait", &trait_ref.print_only_trait_path());
         }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index d18d0fc16a8..3fee2ab6afe 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -11,7 +11,7 @@ use std::sync::Arc;
 use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind};
 use rustc_ast::{
     self as ast, AssocItem, AssocItemKind, Block, ConstItem, Delegation, Fn, ForeignItem,
-    ForeignItemKind, Impl, Item, ItemKind, NodeId, StaticItem, StmtKind, TyAlias,
+    ForeignItemKind, Item, ItemKind, NodeId, StaticItem, StmtKind, TyAlias,
 };
 use rustc_attr_parsing as attr;
 use rustc_attr_parsing::AttributeParser;
@@ -906,10 +906,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             }
 
             // These items do not add names to modules.
-            ItemKind::Impl(box Impl { of_trait: Some(..), .. })
-            | ItemKind::Impl { .. }
-            | ItemKind::ForeignMod(..)
-            | ItemKind::GlobalAsm(..) => {}
+            ItemKind::Impl { .. } | ItemKind::ForeignMod(..) | ItemKind::GlobalAsm(..) => {}
 
             ItemKind::MacroDef(..) | ItemKind::MacCall(_) | ItemKind::DelegationMac(..) => {
                 unreachable!()
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 953b72fd72b..e52cbeb733a 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2620,7 +2620,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.resolve_adt(item, generics);
             }
 
-            ItemKind::Impl(box Impl {
+            ItemKind::Impl(Impl {
                 ref generics,
                 ref of_trait,
                 ref self_ty,
@@ -2631,7 +2631,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.resolve_implementation(
                     &item.attrs,
                     generics,
-                    of_trait,
+                    of_trait.as_deref(),
                     self_ty,
                     item.id,
                     impl_items,
@@ -3177,7 +3177,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         &mut self,
         attrs: &[ast::Attribute],
         generics: &'ast Generics,
-        opt_trait_reference: &'ast Option<TraitRef>,
+        of_trait: Option<&'ast ast::TraitImplHeader>,
         self_type: &'ast Ty,
         item_id: NodeId,
         impl_items: &'ast [Box<AssocItem>],
@@ -3201,7 +3201,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                         |this| {
                             // Resolve the trait reference, if necessary.
                             this.with_optional_trait_ref(
-                                opt_trait_reference.as_ref(),
+                                of_trait.map(|t| &t.trait_ref),
                                 self_type,
                                 |this, trait_id| {
                                     this.resolve_doc_links(attrs, MaybeExported::Impl(trait_id));
@@ -3224,9 +3224,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                                         is_trait_impl: trait_id.is_some()
                                     };
                                     this.with_self_rib(res, |this| {
-                                        if let Some(trait_ref) = opt_trait_reference.as_ref() {
+                                        if let Some(of_trait) = of_trait {
                                             // Resolve type arguments in the trait path.
-                                            visit::walk_trait_ref(this, trait_ref);
+                                            visit::walk_trait_ref(this, &of_trait.trait_ref);
                                         }
                                         // Resolve the self type.
                                         this.visit_ty(self_type);
@@ -5183,7 +5183,7 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> {
             | ItemKind::Enum(_, generics, _)
             | ItemKind::Struct(_, generics, _)
             | ItemKind::Union(_, generics, _)
-            | ItemKind::Impl(box Impl { generics, .. })
+            | ItemKind::Impl(Impl { generics, .. })
             | ItemKind::Trait(box Trait { generics, .. })
             | ItemKind::TraitAlias(_, generics, _) => {
                 if let ItemKind::Fn(box Fn { sig, .. }) = &item.kind {
diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs
index da9f96ce37d..ecc74264160 100644
--- a/compiler/rustc_target/src/spec/base/apple/mod.rs
+++ b/compiler/rustc_target/src/spec/base/apple/mod.rs
@@ -1,5 +1,4 @@
 use std::borrow::Cow;
-use std::env;
 use std::fmt::{Display, from_fn};
 use std::num::ParseIntError;
 use std::str::FromStr;
@@ -209,29 +208,10 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
     // that's only applicable to cross-OS compilation. Always leave anything for the
     // host OS alone though.
     if os == "macos" {
-        let mut env_remove = Vec::with_capacity(2);
-        // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
-        // may occur when we're linking a custom build script while targeting iOS for example.
-        if let Ok(sdkroot) = env::var("SDKROOT") {
-            if sdkroot.contains("iPhoneOS.platform")
-                || sdkroot.contains("iPhoneSimulator.platform")
-                || sdkroot.contains("AppleTVOS.platform")
-                || sdkroot.contains("AppleTVSimulator.platform")
-                || sdkroot.contains("WatchOS.platform")
-                || sdkroot.contains("WatchSimulator.platform")
-                || sdkroot.contains("XROS.platform")
-                || sdkroot.contains("XRSimulator.platform")
-            {
-                env_remove.push("SDKROOT".into())
-            }
-        }
-        // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
+        // `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
         // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
         // although this is apparently ignored when using the linker at "/usr/bin/ld".
-        env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
-        env_remove.push("TVOS_DEPLOYMENT_TARGET".into());
-        env_remove.push("XROS_DEPLOYMENT_TARGET".into());
-        env_remove.into()
+        cvs!["IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET", "XROS_DEPLOYMENT_TARGET"]
     } else {
         // Otherwise if cross-compiling for a different OS/SDK (including Mac Catalyst), remove any part
         // of the linking environment that's wrong and reversed.
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index ae72178c052..bc8c8a44405 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3471,8 +3471,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         ..
                     })) => {
                         let mut spans = Vec::with_capacity(2);
-                        if let Some(trait_ref) = of_trait {
-                            spans.push(trait_ref.path.span);
+                        if let Some(of_trait) = of_trait {
+                            spans.push(of_trait.trait_ref.path.span);
                         }
                         spans.push(self_ty.span);
                         let mut spans: MultiSpan = spans.into();
@@ -3480,7 +3480,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             self_ty.span.ctxt().outer_expn_data().kind,
                             ExpnKind::Macro(MacroKind::Derive, _)
                         ) || matches!(
-                            of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind),
+                            of_trait.map(|t| t.trait_ref.path.span.ctxt().outer_expn_data().kind),
                             Some(ExpnKind::Macro(MacroKind::Derive, _))
                         ) {
                             spans.push_span_label(
@@ -3592,7 +3592,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         ..
                     })) => {
                         let mut spans = vec![self_ty.span];
-                        spans.extend(of_trait.as_ref().map(|t| t.path.span));
+                        spans.extend(of_trait.map(|t| t.trait_ref.path.span));
                         let mut spans: MultiSpan = spans.into();
                         spans.push_span_label(data.span, "unsatisfied trait bound introduced here");
                         err.span_note(spans, msg);
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index 37cb64511c7..e9629e31482 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -174,10 +174,10 @@ fn associated_types_for_impl_traits_in_trait_or_impl<'tcx>(
             })
             .collect(),
         ItemKind::Impl(impl_) => {
-            let Some(trait_ref) = impl_.of_trait else {
+            let Some(of_trait) = impl_.of_trait else {
                 return Default::default();
             };
-            let Some(trait_def_id) = trait_ref.trait_def_id() else {
+            let Some(trait_def_id) = of_trait.trait_ref.trait_def_id() else {
                 return Default::default();
             };
             let in_trait_def = tcx.associated_types_for_impl_traits_in_trait_or_impl(trait_def_id);
diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs
index 6fa763f18ef..cdfb93c4e7d 100644
--- a/compiler/rustc_ty_utils/src/implied_bounds.rs
+++ b/compiler/rustc_ty_utils/src/implied_bounds.rs
@@ -172,10 +172,12 @@ fn impl_spans(tcx: TyCtxt<'_>, def_id: LocalDefId) -> impl Iterator<Item = Span>
         let trait_args = impl_
             .of_trait
             .into_iter()
-            .flat_map(|trait_ref| trait_ref.path.segments.last().unwrap().args().args)
+            .flat_map(|of_trait| of_trait.trait_ref.path.segments.last().unwrap().args().args)
             .map(|arg| arg.span());
-        let dummy_spans_for_default_args =
-            impl_.of_trait.into_iter().flat_map(|trait_ref| iter::repeat(trait_ref.path.span));
+        let dummy_spans_for_default_args = impl_
+            .of_trait
+            .into_iter()
+            .flat_map(|of_trait| iter::repeat(of_trait.trait_ref.path.span));
         iter::once(impl_.self_ty.span).chain(trait_args).chain(dummy_spans_for_default_args)
     } else {
         bug!("unexpected item for impl {def_id:?}: {item:?}")
diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs
index dc6009116ac..d95660810e5 100644
--- a/compiler/rustc_ty_utils/src/sig_types.rs
+++ b/compiler/rustc_ty_utils/src/sig_types.rs
@@ -87,7 +87,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>(
         DefKind::InlineConst | DefKind::Closure | DefKind::SyntheticCoroutineBody => {}
         DefKind::Impl { of_trait } => {
             if of_trait {
-                let span = tcx.hir_node_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().path.span;
+                let span = tcx.hir_node_by_def_id(item).expect_item().expect_impl().of_trait.unwrap().trait_ref.path.span;
                 let args = &tcx.impl_trait_ref(item).unwrap().instantiate_identity().args[1..];
                 try_visit!(visitor.visit(span, args));
             }
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 2e0b16d9227..b22c326b9f2 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -81,7 +81,11 @@ fn sizedness_constraint_for_ty<'tcx>(
 fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
     match tcx.hir_node_by_def_id(def_id) {
         hir::Node::Item(hir::Item {
-            kind: hir::ItemKind::Impl(hir::Impl { defaultness, of_trait: Some(_), .. }),
+            kind:
+                hir::ItemKind::Impl(hir::Impl {
+                    of_trait: Some(hir::TraitImplHeader { defaultness, .. }),
+                    ..
+                }),
             ..
         })
         | hir::Node::ImplItem(hir::ImplItem { defaultness, .. })
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 2321aab2c51..8001faf9d20 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -49,7 +49,27 @@
 //! v[1] = v[1] + 5;
 //! ```
 //!
+//! # Memory layout
+//!
+//! When the type is non-zero-sized and the capacity is nonzero, [`Vec`] uses the [`Global`]
+//! allocator for its allocation. It is valid to convert both ways between such a [`Vec`] and a raw
+//! pointer allocated with the [`Global`] allocator, provided that the [`Layout`] used with the
+//! allocator is correct for a sequence of `capacity` elements of the type, and the first `len`
+//! values pointed to by the raw pointer are valid. More precisely, a `ptr: *mut T` that has been
+//! allocated with the [`Global`] allocator with [`Layout::array::<T>(capacity)`][Layout::array] may
+//! be converted into a vec using
+//! [`Vec::<T>::from_raw_parts(ptr, len, capacity)`](Vec::from_raw_parts). Conversely, the memory
+//! backing a `value: *mut T` obtained from [`Vec::<T>::as_mut_ptr`] may be deallocated using the
+//! [`Global`] allocator with the same layout.
+//!
+//! For zero-sized types (ZSTs), or when the capacity is zero, the `Vec` pointer must be non-null
+//! and sufficiently aligned. The recommended way to build a `Vec` of ZSTs if [`vec!`] cannot be
+//! used is to use [`ptr::NonNull::dangling`].
+//!
 //! [`push`]: Vec::push
+//! [`ptr::NonNull::dangling`]: NonNull::dangling
+//! [`Layout`]: crate::alloc::Layout
+//! [Layout::array]: crate::alloc::Layout::array
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
@@ -523,18 +543,23 @@ impl<T> Vec<T> {
     /// This is highly unsafe, due to the number of invariants that aren't
     /// checked:
     ///
-    /// * `ptr` must have been allocated using the global allocator, such as via
-    ///   the [`alloc::alloc`] function.
-    /// * `T` needs to have the same alignment as what `ptr` was allocated with.
+    /// * If `T` is not a zero-sized type and the capacity is nonzero, `ptr` must have
+    ///   been allocated using the global allocator, such as via the [`alloc::alloc`]
+    ///   function. If `T` is a zero-sized type or the capacity is zero, `ptr` need
+    ///   only be non-null and aligned.
+    /// * `T` needs to have the same alignment as what `ptr` was allocated with,
+    ///   if the pointer is required to be allocated.
     ///   (`T` having a less strict alignment is not sufficient, the alignment really
     ///   needs to be equal to satisfy the [`dealloc`] requirement that memory must be
     ///   allocated and deallocated with the same layout.)
-    /// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs
-    ///   to be the same size as the pointer was allocated with. (Because similar to
-    ///   alignment, [`dealloc`] must be called with the same layout `size`.)
+    /// * The size of `T` times the `capacity` (ie. the allocated size in bytes), if
+    ///   nonzero, needs to be the same size as the pointer was allocated with.
+    ///   (Because similar to alignment, [`dealloc`] must be called with the same
+    ///   layout `size`.)
     /// * `length` needs to be less than or equal to `capacity`.
     /// * The first `length` values must be properly initialized values of type `T`.
-    /// * `capacity` needs to be the capacity that the pointer was allocated with.
+    /// * `capacity` needs to be the capacity that the pointer was allocated with,
+    ///   if the pointer is required to be allocated.
     /// * The allocated size in bytes must be no larger than `isize::MAX`.
     ///   See the safety documentation of [`pointer::offset`].
     ///
@@ -770,12 +795,16 @@ impl<T> Vec<T> {
     /// order as the arguments to [`from_raw_parts`].
     ///
     /// After calling this function, the caller is responsible for the
-    /// memory previously managed by the `Vec`. The only way to do
-    /// this is to convert the raw pointer, length, and capacity back
-    /// into a `Vec` with the [`from_raw_parts`] function, allowing
-    /// the destructor to perform the cleanup.
+    /// memory previously managed by the `Vec`. Most often, one does
+    /// this by converting the raw pointer, length, and capacity back
+    /// into a `Vec` with the [`from_raw_parts`] function; more generally,
+    /// if `T` is non-zero-sized and the capacity is nonzero, one may use
+    /// any method that calls [`dealloc`] with a layout of
+    /// `Layout::array::<T>(capacity)`; if `T` is zero-sized or the
+    /// capacity is zero, nothing needs to be done.
     ///
     /// [`from_raw_parts`]: Vec::from_raw_parts
+    /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
     ///
     /// # Examples
     ///
@@ -1755,6 +1784,12 @@ impl<T, A: Allocator> Vec<T, A> {
     /// may still invalidate this pointer.
     /// See the second example below for how this guarantee can be used.
     ///
+    /// The method also guarantees that, as long as `T` is not zero-sized and the capacity is
+    /// nonzero, the pointer may be passed into [`dealloc`] with a layout of
+    /// `Layout::array::<T>(capacity)` in order to deallocate the backing memory. If this is done,
+    /// be careful not to run the destructor of the `Vec`, as dropping it will result in
+    /// double-frees. Wrapping the `Vec` in a [`ManuallyDrop`] is the typical way to achieve this.
+    ///
     /// # Examples
     ///
     /// ```
@@ -1787,9 +1822,24 @@ impl<T, A: Allocator> Vec<T, A> {
     /// }
     /// ```
     ///
+    /// Deallocating a vector using [`Box`] (which uses [`dealloc`] internally):
+    ///
+    /// ```
+    /// use std::mem::{ManuallyDrop, MaybeUninit};
+    ///
+    /// let mut v = ManuallyDrop::new(vec![0, 1, 2]);
+    /// let ptr = v.as_mut_ptr();
+    /// let capacity = v.capacity();
+    /// let slice_ptr: *mut [MaybeUninit<i32>] =
+    ///     std::ptr::slice_from_raw_parts_mut(ptr.cast(), capacity);
+    /// drop(unsafe { Box::from_raw(slice_ptr) });
+    /// ```
+    ///
     /// [`as_mut_ptr`]: Vec::as_mut_ptr
     /// [`as_ptr`]: Vec::as_ptr
     /// [`as_non_null`]: Vec::as_non_null
+    /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
+    /// [`ManuallyDrop`]: core::mem::ManuallyDrop
     #[stable(feature = "vec_as_ptr", since = "1.37.0")]
     #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")]
     #[rustc_never_returns_null_ptr]
diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs
index 49e75a1a70d..56ca999cc7e 100644
--- a/library/std/src/sys/pal/uefi/tests.rs
+++ b/library/std/src/sys/pal/uefi/tests.rs
@@ -1,8 +1,13 @@
+//! These tests are not run automatically right now. Please run these tests manually by copying them
+//! to a separate project when modifying any related code.
+
 use super::alloc::*;
-use super::time::*;
+use super::time::system_time_internal::{from_uefi, to_uefi};
 use crate::io::{IoSlice, IoSliceMut};
 use crate::time::Duration;
 
+const SECS_IN_MINUTE: u64 = 60;
+
 #[test]
 fn align() {
     // UEFI ABI specifies that allocation alignment minimum is always 8. So this can be
@@ -24,21 +29,61 @@ fn align() {
 }
 
 #[test]
-fn epoch() {
-    let t = r_efi::system::Time {
-        year: 1970,
+fn systemtime_start() {
+    let t = r_efi::efi::Time {
+        year: 1900,
+        month: 1,
+        day: 1,
+        hour: 0,
+        minute: 0,
+        second: 0,
+        nanosecond: 0,
+        timezone: -1440,
+        daylight: 0,
+        pad2: 0,
+    };
+    assert_eq!(from_uefi(&t), Duration::new(0, 0));
+    assert_eq!(t, to_uefi(&from_uefi(&t), -1440, 0).unwrap());
+    assert!(to_uefi(&from_uefi(&t), 0, 0).is_none());
+}
+
+#[test]
+fn systemtime_utc_start() {
+    let t = r_efi::efi::Time {
+        year: 1900,
         month: 1,
         day: 1,
         hour: 0,
         minute: 0,
         second: 0,
+        pad1: 0,
         nanosecond: 0,
-        timezone: r_efi::efi::UNSPECIFIED_TIMEZONE,
+        timezone: 0,
         daylight: 0,
+        pad2: 0,
+    };
+    assert_eq!(from_uefi(&t), Duration::new(1440 * SECS_IN_MINUTE, 0));
+    assert_eq!(t, to_uefi(&from_uefi(&t), 0, 0).unwrap());
+    assert!(to_uefi(&from_uefi(&t), -1440, 0).is_some());
+}
+
+#[test]
+fn systemtime_end() {
+    let t = r_efi::efi::Time {
+        year: 9999,
+        month: 12,
+        day: 31,
+        hour: 23,
+        minute: 59,
+        second: 59,
         pad1: 0,
+        nanosecond: 0,
+        timezone: 1440,
+        daylight: 0,
         pad2: 0,
     };
-    assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0));
+    assert!(to_uefi(&from_uefi(&t), 1440, 0).is_some());
+    assert!(to_uefi(&from_uefi(&t), 1439, 0).is_none());
 }
 
 // UEFI IoSlice and IoSliceMut Tests
diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs
index eeb2c35ffbb..a5ab7690327 100644
--- a/library/std/src/sys/pal/uefi/time.rs
+++ b/library/std/src/sys/pal/uefi/time.rs
@@ -1,16 +1,42 @@
 use crate::time::Duration;
 
-const SECS_IN_MINUTE: u64 = 60;
-const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
-const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
-
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
 pub struct Instant(Duration);
 
+/// When a Timezone is specified, the stored Duration is in UTC. If timezone is unspecified, then
+/// the timezone is assumed to be in UTC.
+///
+/// UEFI SystemTime is stored as Duration from 1900-01-01-00:00:00 with timezone -1440 as anchor
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
 pub struct SystemTime(Duration);
 
-pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
+pub const UNIX_EPOCH: SystemTime = SystemTime::from_uefi(r_efi::efi::Time {
+    year: 1970,
+    month: 1,
+    day: 1,
+    hour: 0,
+    minute: 0,
+    second: 0,
+    nanosecond: 0,
+    timezone: 0,
+    daylight: 0,
+    pad1: 0,
+    pad2: 0,
+});
+
+const MAX_UEFI_TIME: SystemTime = SystemTime::from_uefi(r_efi::efi::Time {
+    year: 9999,
+    month: 12,
+    day: 31,
+    hour: 23,
+    minute: 59,
+    second: 59,
+    nanosecond: 999_999_999,
+    timezone: 1440,
+    daylight: 0,
+    pad1: 0,
+    pad2: 0,
+});
 
 impl Instant {
     pub fn now() -> Instant {
@@ -40,6 +66,15 @@ impl Instant {
 }
 
 impl SystemTime {
+    pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Self {
+        Self(system_time_internal::from_uefi(&t))
+    }
+
+    #[expect(dead_code)]
+    pub(crate) const fn to_uefi(self, timezone: i16, daylight: u8) -> Option<r_efi::efi::Time> {
+        system_time_internal::to_uefi(&self.0, timezone, daylight)
+    }
+
     pub fn now() -> SystemTime {
         system_time_internal::now()
             .unwrap_or_else(|| panic!("time not implemented on this platform"))
@@ -50,11 +85,14 @@ impl SystemTime {
     }
 
     pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
-        Some(SystemTime(self.0.checked_add(*other)?))
+        let temp = Self(self.0.checked_add(*other)?);
+
+        // Check if can be represented in UEFI
+        if temp <= MAX_UEFI_TIME { Some(temp) } else { None }
     }
 
     pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
-        Some(SystemTime(self.0.checked_sub(*other)?))
+        self.0.checked_sub(*other).map(Self)
     }
 }
 
@@ -66,51 +104,132 @@ pub(crate) mod system_time_internal {
     use crate::mem::MaybeUninit;
     use crate::ptr::NonNull;
 
+    const SECS_IN_MINUTE: u64 = 60;
+    const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
+    const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
+    const TIMEZONE_DELTA: u64 = 1440 * SECS_IN_MINUTE;
+
     pub fn now() -> Option<SystemTime> {
         let runtime_services: NonNull<RuntimeServices> = helpers::runtime_services()?;
         let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
         let r = unsafe {
             ((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut())
         };
-
         if r.is_error() {
             return None;
         }
 
         let t = unsafe { t.assume_init() };
 
-        Some(SystemTime(uefi_time_to_duration(t)))
+        Some(SystemTime::from_uefi(t))
     }
 
-    // This algorithm is based on the one described in the post
-    // https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
-    pub(crate) const fn uefi_time_to_duration(t: r_efi::system::Time) -> Duration {
-        assert!(t.month <= 12);
-        assert!(t.month != 0);
+    /// This algorithm is a modified form of the one described in the post
+    /// https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
+    ///
+    /// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
+    /// epoch used in the original algorithm.
+    pub(crate) const fn from_uefi(t: &Time) -> Duration {
+        assert!(t.month <= 12 && t.month != 0);
+        assert!(t.year >= 1900 && t.year <= 9999);
+        assert!(t.day <= 31 && t.day != 0);
+
+        assert!(t.second < 60);
+        assert!(t.minute < 60);
+        assert!(t.hour < 24);
+        assert!(t.nanosecond < 1_000_000_000);
+
+        assert!(
+            (t.timezone <= 1440 && t.timezone >= -1440)
+                || t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE
+        );
 
         const YEAR_BASE: u32 = 4800; /* Before min year, multiple of 400. */
 
-        // Calculate the number of days since 1/1/1970
+        // Calculate the number of days since 1/1/1900. This is the earliest supported date in UEFI
+        // time.
         // Use 1 March as the start
         let (m_adj, overflow): (u32, bool) = (t.month as u32).overflowing_sub(3);
         let (carry, adjust): (u32, u32) = if overflow { (1, 12) } else { (0, 0) };
         let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry;
         let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048;
         let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400;
-        let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2472632;
+        let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2447065;
 
         let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY
             + (t.second as u64)
             + (t.minute as u64) * SECS_IN_MINUTE
             + (t.hour as u64) * SECS_IN_HOUR;
 
-        let utc_epoch: u64 = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
-            localtime_epoch
+        // Calculate the offset from 1/1/1900 at timezone -1440 min
+        let adjusted_localtime_epoc: u64 = localtime_epoch + TIMEZONE_DELTA;
+
+        let epoch: u64 = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
+            adjusted_localtime_epoc
         } else {
-            (localtime_epoch as i64 + (t.timezone as i64) * SECS_IN_MINUTE as i64) as u64
+            adjusted_localtime_epoc
+                .checked_add_signed((t.timezone as i64) * SECS_IN_MINUTE as i64)
+                .unwrap()
         };
 
-        Duration::new(utc_epoch, t.nanosecond)
+        Duration::new(epoch, t.nanosecond)
+    }
+
+    /// This algorithm is a modifed version of the one described in the post:
+    /// https://howardhinnant.github.io/date_algorithms.html#clive_from_days
+    ///
+    /// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
+    /// epoch used in the original algorithm.
+    pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Option<Time> {
+        // Check timzone validity
+        assert!(timezone <= 1440 && timezone >= -1440);
+
+        // FIXME(#126043): use checked_sub_signed once stablized
+        let secs =
+            dur.as_secs().checked_add_signed((-timezone as i64) * SECS_IN_MINUTE as i64).unwrap();
+
+        // Convert to seconds since 1900-01-01-00:00:00 in timezone.
+        let Some(secs) = secs.checked_sub(TIMEZONE_DELTA) else { return None };
+
+        let days = secs / SECS_IN_DAY;
+        let remaining_secs = secs % SECS_IN_DAY;
+
+        let z = days + 693901;
+        let era = z / 146097;
+        let doe = z - (era * 146097);
+        let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
+        let mut y = yoe + era * 400;
+        let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
+        let mp = (5 * doy + 2) / 153;
+        let d = doy - (153 * mp + 2) / 5 + 1;
+        let m = if mp < 10 { mp + 3 } else { mp - 9 };
+
+        if m <= 2 {
+            y += 1;
+        }
+
+        let hour = (remaining_secs / SECS_IN_HOUR) as u8;
+        let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8;
+        let second = (remaining_secs % SECS_IN_MINUTE) as u8;
+
+        // Check Bounds
+        if y >= 1900 && y <= 9999 {
+            Some(Time {
+                year: y as u16,
+                month: m as u8,
+                day: d as u8,
+                hour,
+                minute,
+                second,
+                nanosecond: dur.subsec_nanos(),
+                timezone,
+                daylight,
+                pad1: 0,
+                pad2: 0,
+            })
+        } else {
+            None
+        }
     }
 }
 
diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile
index 1a219125593..6fea2437276 100644
--- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile
+++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile
@@ -33,11 +33,11 @@ ENV SCRIPT \
         python3 ../x.py test --stage 1 src/tools/compiletest && \
         python3 ../x.py doc bootstrap && \
         # Build both public and internal documentation.
-        RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc compiler --stage 2 && \
-        RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library --stage 2 && \
+        RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc compiler --stage 1 && \
+        RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library --stage 1 && \
         mkdir -p /checkout/obj/staging/doc && \
         cp -r build/x86_64-unknown-linux-gnu/doc /checkout/obj/staging && \
-        RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library/test --stage 2 && \
+        RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library/test --stage 1 && \
         # The BOOTSTRAP_TRACING flag is added to verify whether the
         # bootstrap process compiles successfully with this flag enabled.
         BOOTSTRAP_TRACING=1 python3 ../x.py --help
diff --git a/src/doc/reference b/src/doc/reference
-Subproject 1be151c051a082b542548c62cafbcb055fa8944
+Subproject 59b8af811886313577615c2cf0e045f01faed88
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject bd1279cdc9865bfff605e741fb76a0b2f07314a
+Subproject adc1f3b9012ad3255eea2054ca30596a953d053
diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md
index c26d34273d7..3c4c65fdd5a 100644
--- a/src/doc/rustc-dev-guide/src/stability.md
+++ b/src/doc/rustc-dev-guide/src/stability.md
@@ -182,6 +182,11 @@ of the standard library raises it to a warning with
 `#![warn(deprecated_in_future)]`.
 
 ## unstable_feature_bound
-The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. In std/core, an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate. Currently, only `impl`s and free functions can be annotated with `#[unstable_feature_bound]`.
+The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. In std/core, an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate.
+
+Currently, the items that can be annotated with `#[unstable_feature_bound]` are:
+- `impl`
+- free function
+- trait
 
 [blog]: https://www.ralfj.de/blog/2018/07/19/const.html
diff --git a/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md b/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md
index be9ed02f54d..6d371ae289f 100644
--- a/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md
+++ b/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md
@@ -1,6 +1,6 @@
 # `SDKROOT`
 
 This environment variable is used on Apple targets.
-It is passed through to the linker (currently either as `-isysroot` or `-syslibroot`).
+It is passed through to the linker (currently either directly or via the `-syslibroot` flag).
 
 Note that this variable is not always respected. When the SDKROOT is clearly wrong (e.g. when the platform of the SDK does not match the `--target` used by rustc), this is ignored and rustc does its own detection.
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 7194c2fcede..890bfaced6c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -2768,7 +2768,7 @@ fn clean_maybe_renamed_item<'tcx>(
         // These kinds of item either don't need a `name` or accept a `None` one so we handle them
         // before.
         match item.kind {
-            ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx),
+            ItemKind::Impl(ref impl_) => return clean_impl(impl_, item.owner_id.def_id, cx),
             ItemKind::Use(path, kind) => {
                 return clean_use_statement(
                     item,
@@ -2896,7 +2896,7 @@ fn clean_impl<'tcx>(
 ) -> Vec<Item> {
     let tcx = cx.tcx;
     let mut ret = Vec::new();
-    let trait_ = impl_.of_trait.as_ref().map(|t| clean_trait_ref(t, cx));
+    let trait_ = impl_.of_trait.map(|t| clean_trait_ref(&t.trait_ref, cx));
     let items = impl_
         .items
         .iter()
@@ -2922,7 +2922,10 @@ fn clean_impl<'tcx>(
         });
     let mut make_item = |trait_: Option<Path>, for_: Type, items: Vec<Item>| {
         let kind = ImplItem(Box::new(Impl {
-            safety: impl_.safety,
+            safety: match impl_.of_trait {
+                Some(of_trait) => of_trait.safety,
+                None => hir::Safety::Safe,
+            },
             generics: clean_generics(impl_.generics, cx),
             trait_,
             for_,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 02ee34aaac6..759f53974f5 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1656,11 +1656,9 @@ fn display_c_like_variant(
         } else if should_show_enum_discriminant {
             let adt_def = cx.tcx().adt_def(enum_def_id);
             let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
-            if discr.ty.is_signed() {
-                write!(w, "{} = {}", name.as_str(), discr.val as i128)?;
-            } else {
-                write!(w, "{} = {}", name.as_str(), discr.val)?;
-            }
+            // Use `discr`'s `Display` impl to render the value with the correct
+            // signedness, including proper sign-extension for signed types.
+            write!(w, "{} = {}", name.as_str(), discr)?;
         } else {
             write!(w, "{name}")?;
         }
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index bcb676cd1f1..40191551e4f 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -997,6 +997,7 @@ fn preprocess_link(
         }
     };
 
+    let is_shortcut_style = ori_link.kind == LinkType::ShortcutUnknown;
     // If there's no backticks, be lenient and revert to the old behavior.
     // This is to prevent churn by linting on stuff that isn't meant to be a link.
     // only shortcut links have simple enough syntax that they
@@ -1013,11 +1014,22 @@ fn preprocess_link(
     // | has backtick |    never ignore    |    never ignore   |
     // | no backtick  | ignore if url-like |    never ignore   |
     // |-------------------------------------------------------|
-    let ignore_urllike =
-        can_be_url || (ori_link.kind == LinkType::ShortcutUnknown && !ori_link.link.contains('`'));
+    let ignore_urllike = can_be_url || (is_shortcut_style && !ori_link.link.contains('`'));
     if ignore_urllike && should_ignore_link(path_str) {
         return None;
     }
+    // If we have an intra-doc link starting with `!` (which isn't `[!]` because this is the never type), we ignore it
+    // as it is never valid.
+    //
+    // The case is common enough because of cases like `#[doc = include_str!("../README.md")]` which often
+    // uses GitHub-flavored Markdown (GFM) admonitions, such as `[!NOTE]`.
+    if is_shortcut_style
+        && let Some(suffix) = ori_link.link.strip_prefix('!')
+        && !suffix.is_empty()
+        && suffix.chars().all(|c| c.is_ascii_alphabetic())
+    {
+        return None;
+    }
 
     // Strip generics from the path.
     let path_str = match strip_generics_from_path(path_str) {
diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
index d6469d32931..36498adff50 100644
--- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
+++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
@@ -534,6 +534,7 @@ fn get_item_name(item: &Item<'_>) -> Option<String> {
 
                         if let Some(of_trait) = im.of_trait {
                             let mut trait_segs: Vec<String> = of_trait
+                                .trait_ref
                                 .path
                                 .segments
                                 .iter()
diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
index 581fe33ea0b..f31b67f470f 100644
--- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -130,18 +130,22 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
 
                 let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())];
 
-                if bool_value ^ eq_macro {
-                    let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else {
-                        return;
+                if let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) {
+                    let sugg = if bool_value ^ eq_macro {
+                        !sugg.maybe_paren()
+                    } else if ty::Bool == *non_lit_ty.kind() {
+                        sugg
+                    } else {
+                        !!sugg.maybe_paren()
                     };
-                    suggestions.push((non_lit_expr.span, (!sugg).to_string()));
-                }
+                    suggestions.push((non_lit_expr.span, sugg.to_string()));
 
-                diag.multipart_suggestion(
-                    format!("replace it with `{non_eq_mac}!(..)`"),
-                    suggestions,
-                    Applicability::MachineApplicable,
-                );
+                    diag.multipart_suggestion(
+                        format!("replace it with `{non_eq_mac}!(..)`"),
+                        suggestions,
+                        Applicability::MachineApplicable,
+                    );
+                }
             },
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
index 4ecf3e41611..51aebd8b0cf 100644
--- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
@@ -37,12 +37,12 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]);
 impl<'tcx> LateLintPass<'tcx> for CopyIterator {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             ..
         }) = item.kind
             && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
             && is_copy(cx, ty)
-            && let Some(trait_id) = trait_ref.trait_def_id()
+            && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
             && cx.tcx.is_diagnostic_item(sym::Iterator, trait_id)
         {
             span_lint_and_note(
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index 0a481ddcd12..7580d6cab66 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -183,14 +183,14 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex
 impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             items: [child],
             self_ty,
             ..
         }) = item.kind
             && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id())
             && !item.span.from_expansion()
-            && let Some(def_id) = trait_ref.trait_def_id()
+            && let Some(def_id) = of_trait.trait_ref.trait_def_id()
             && cx.tcx.is_diagnostic_item(sym::Default, def_id)
             && let impl_item_hir = child.hir_id()
             && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir)
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 49dd1bb09c6..c53a957f6a8 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -201,10 +201,11 @@ declare_lint_pass!(Derive => [
 impl<'tcx> LateLintPass<'tcx> for Derive {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             ..
         }) = item.kind
         {
+            let trait_ref = &of_trait.trait_ref;
             let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
             let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id());
 
diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs
index 4e948701da4..2b822188434 100644
--- a/src/tools/clippy/clippy_lints/src/empty_drop.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs
@@ -36,11 +36,11 @@ declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
 impl LateLintPass<'_> for EmptyDrop {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             items: [child],
             ..
         }) = item.kind
-            && trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait()
+            && of_trait.trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait()
             && let impl_item_hir = child.hir_id()
             && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir)
             && let ImplItemKind::Fn(_, b) = &impl_item.kind
diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
index 6525648efb1..3018e1f1273 100644
--- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs
+++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
@@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
                 );
             },
             ItemKind::Impl(imp)
-                if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id())
+                if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_ref.trait_def_id())
                     && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error)
                     && error_def_id == trait_def_id
                     && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local)
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index 0535ecf5240..416aea51ea1 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -254,10 +254,10 @@ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Optio
     if impl_item.ident.name == sym::fmt
         && let ImplItemKind::Fn(_, body_id) = impl_item.kind
         && let Some(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             ..
         }) = get_parent_as_impl(cx.tcx, impl_item.hir_id())
-        && let Some(did) = trait_ref.trait_def_id()
+        && let Some(did) = of_trait.trait_ref.trait_def_id()
         && let Some(name) = cx.tcx.get_diagnostic_name(did)
         && matches!(name, sym::Debug | sym::Display)
     {
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index 1da6952eb64..e3bb5ee10db 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -67,12 +67,12 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
 impl<'tcx> LateLintPass<'tcx> for FromOverInto {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(hir_trait_ref),
+            of_trait: Some(of_trait),
             self_ty,
             items: [impl_item_ref],
             ..
         }) = item.kind
-            && let Some(into_trait_seg) = hir_trait_ref.path.segments.last()
+            && let Some(into_trait_seg) = of_trait.trait_ref.path.segments.last()
             // `impl Into<target_ty> for self_ty`
             && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
             && span_is_local(item.span)
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index cb83b1395d2..3105e303ae3 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -54,8 +54,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
     if let ImplItemKind::Fn(_, body_id) = impl_item.kind
         && let hir::Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id())
         && let hir::ItemKind::Impl(impl_) = item.kind
-        && let hir::Impl { of_trait, .. } = *impl_
-        && of_trait.is_none()
+        && let hir::Impl { of_trait: None, .. } = impl_
         && let body = cx.tcx.hir_body(body_id)
         && cx.tcx.visibility(cx.tcx.hir_body_owner_def_id(body.id())).is_public()
         && !is_in_test(cx.tcx, impl_item.hir_id())
diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
index 0d6191f2c97..0a7c6e9d5f8 100644
--- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
@@ -15,11 +15,11 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored
         && let parent_node = cx.tcx.parent_hir_node(item.hir_id())
         && let Node::Item(parent_item) = parent_node
         && let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             ..
         }) = &parent_item.kind
         && let Some(did) = trait_item_def_id_of_impl(cx, item.owner_id)
-        && !is_from_ignored_trait(trait_ref, ignored_traits)
+        && !is_from_ignored_trait(&of_trait.trait_ref, ignored_traits)
     {
         let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id);
         let mut default_param_idents_iter = cx.tcx.fn_arg_idents(did).iter().copied();
diff --git a/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs b/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs
index 940adbae428..f73182d3af0 100644
--- a/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs
+++ b/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::implements_trait;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Item, ItemKind, Path, TraitRef};
+use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 use rustc_session::declare_lint_pass;
@@ -76,10 +76,10 @@ impl LateLintPass<'_> for ImplHashWithBorrowStrBytes {
     /// three of `Hash`, `Borrow<str>` and `Borrow<[u8]>`.
     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
         if let ItemKind::Impl(imp) = item.kind
-            && let Some(TraitRef {path: Path {span, res, ..}, ..}) = imp.of_trait
+            && let Some(of_trait) = imp.of_trait
             && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
             && let Some(hash_id) = cx.tcx.get_diagnostic_item(sym::Hash)
-            && Res::Def(DefKind::Trait, hash_id) == *res
+            && Res::Def(DefKind::Trait, hash_id) == of_trait.trait_ref.path.res
             && let Some(borrow_id) = cx.tcx.get_diagnostic_item(sym::Borrow)
             // since we are in the `Hash` impl, we don't need to check for that.
             // we need only to check for `Borrow<str>` and `Borrow<[u8]>`
@@ -89,7 +89,7 @@ impl LateLintPass<'_> for ImplHashWithBorrowStrBytes {
             span_lint_and_then(
                 cx,
                 IMPL_HASH_BORROW_WITH_STR_AND_BYTES,
-                *span,
+                of_trait.trait_ref.path.span,
                 "the semantics of `Borrow<T>` around `Hash` can't be satisfied when both `Borrow<str>` and `Borrow<[u8]>` are implemented",
                 |diag| {
                     diag.note("the `Borrow` semantics require that `Hash` must behave the same for all implementations of Borrow<T>");
diff --git a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
index 589c294a678..36df07a4370 100644
--- a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
+++ b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
@@ -45,8 +45,8 @@ declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]);
 impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         let ItemKind::Impl(imp) = item.kind else { return };
-        let Some(r#trait) = imp.of_trait else { return };
-        let Some(trait_def_id) = r#trait.trait_def_id() else {
+        let Some(of_trait) = imp.of_trait else { return };
+        let Some(trait_def_id) = of_trait.trait_ref.trait_def_id() else {
             return;
         };
         if !cx.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) {
diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
index b89f91f7255..645e0f981f2 100644
--- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
@@ -125,8 +125,9 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
         if let ItemKind::Impl(imp) = item.kind
             && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
-            && let Some(trait_ref) = imp.of_trait
-            && trait_ref
+            && let Some(of_trait) = imp.of_trait
+            && of_trait
+                .trait_ref
                 .trait_def_id()
                 .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did))
             && !item.span.in_external_macro(cx.sess().source_map())
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 35c9d2fd4eb..149ae5e710c 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
         } = item.kind
         {
             check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv);
-        } else if let ItemKind::Impl(impl_) = item.kind
+        } else if let ItemKind::Impl(impl_) = &item.kind
             && !item.span.from_expansion()
         {
             report_extra_impl_lifetimes(cx, impl_);
@@ -712,8 +712,8 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
     let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, impl_.generics);
 
     walk_generics(&mut checker, impl_.generics);
-    if let Some(ref trait_ref) = impl_.of_trait {
-        walk_trait_ref(&mut checker, trait_ref);
+    if let Some(of_trait) = impl_.of_trait {
+        walk_trait_ref(&mut checker, &of_trait.trait_ref);
     }
     walk_unambig_ty(&mut checker, impl_.self_ty);
     for &item in impl_.items {
diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
index 18e2b384a46..8822b32b1c3 100644
--- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
@@ -198,8 +198,8 @@ fn check_struct<'tcx>(
 impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         // is this an `impl Debug for X` block?
-        if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, .. }) = item.kind
-            && let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res
+        if let ItemKind::Impl(Impl { of_trait: Some(of_trait), self_ty, .. }) = item.kind
+            && let Res::Def(DefKind::Trait, trait_def_id) = of_trait.trait_ref.path.res
             && let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind
             // make sure that the self type is either a struct, an enum or a union
             // this prevents ICEs such as when self is a type parameter or a primitive type
diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
index 399bf4e1806..9cc93bf0653 100644
--- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
@@ -61,10 +61,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods {
         if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id())
             && span_is_local(item.span)
             && let ItemKind::Impl(Impl {
-                of_trait: Some(trait_ref),
+                of_trait: Some(of_trait),
                 ..
             }) = item.kind
-            && let Some(trait_id) = trait_ref.trait_def_id()
+            && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
         {
             let trait_item_ids: DefIdSet = cx
                 .tcx
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 388c029c9ef..8a5a6f4a4dc 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -778,7 +778,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
                     if let Node::Item(parent_item) = cx.tcx.parent_hir_node(item.hir_id())
                         && let ItemKind::Impl(impl_block) = parent_item.kind
                         && let Some(of_trait) = impl_block.of_trait
-                        && let Some(trait_id) = of_trait.trait_def_id()
+                        && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
                     {
                         // Replace all instances of `<Self as Trait>::AssocType` with the
                         // unit type and check again. If the result is the same then the
diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
index 8ff78ec7c58..b810bc01fbd 100644
--- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
+++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
@@ -83,10 +83,10 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
         if !item.span.in_external_macro(cx.tcx.sess.source_map())
             && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send)
             && let ItemKind::Impl(hir_impl) = &item.kind
-            && let Some(trait_ref) = &hir_impl.of_trait
-            && let Some(trait_id) = trait_ref.trait_def_id()
+            && let Some(of_trait) = &hir_impl.of_trait
+            && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
             && send_trait == trait_id
-            && hir_impl.polarity == ImplPolarity::Positive
+            && of_trait.polarity == ImplPolarity::Positive
             && let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
             && let self_ty = ty_trait_ref.instantiate_identity().self_ty()
             && let ty::Adt(adt_def, impl_trait_args) = self_ty.kind()
diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
index 0a1f2625f4c..9c160ff680e 100644
--- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
@@ -183,7 +183,7 @@ fn in_impl<'tcx>(
         && let item = cx.tcx.hir_expect_item(impl_def_id.expect_local())
         && let ItemKind::Impl(item) = &item.kind
         && let Some(of_trait) = &item.of_trait
-        && let Some(seg) = of_trait.path.segments.last()
+        && let Some(seg) = of_trait.trait_ref.path.segments.last()
         && let Res::Def(_, trait_id) = seg.res
         && trait_id == bin_op
         && let Some(generic_args) = seg.args
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
index 301b2cd4bf2..77751e75a8e 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
@@ -34,15 +34,15 @@ declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]);
 impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             items: impl_items,
             ..
         }) = item.kind
             && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id())
             && let Some(eq_trait) = cx.tcx.lang_items().eq_trait()
-            && trait_ref.path.res.def_id() == eq_trait
+            && of_trait.trait_ref.path.res.def_id() == eq_trait
         {
-            for impl_item in *impl_items {
+            for impl_item in impl_items {
                 if cx.tcx.item_name(impl_item.owner_id) == sym::ne {
                     span_lint_hir(
                         cx,
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index 67eb71f7d07..b87751f4986 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -68,9 +68,9 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
                 let existing_name = map.get_mut(res).unwrap();
 
                 match of_trait {
-                    Some(trait_ref) => {
+                    Some(of_trait) => {
                         let mut methods_in_trait: BTreeSet<Symbol> = if let Node::TraitRef(TraitRef { path, .. }) =
-                            cx.tcx.hir_node(trait_ref.hir_ref_id)
+                            cx.tcx.hir_node(of_trait.trait_ref.hir_ref_id)
                             && let Res::Def(DefKind::Trait, did) = path.res
                         {
                             // FIXME: if
diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs
index 2de22e4b6a3..01c7f394b9a 100644
--- a/src/tools/clippy/clippy_lints/src/serde_api.rs
+++ b/src/tools/clippy/clippy_lints/src/serde_api.rs
@@ -26,16 +26,16 @@ declare_lint_pass!(SerdeApi => [SERDE_API_MISUSE]);
 impl<'tcx> LateLintPass<'tcx> for SerdeApi {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             items,
             ..
         }) = item.kind
         {
-            let did = trait_ref.path.res.def_id();
+            let did = of_trait.trait_ref.path.res.def_id();
             if paths::SERDE_DE_VISITOR.matches(cx, did) {
                 let mut seen_str = None;
                 let mut seen_string = None;
-                for item in *items {
+                for item in items {
                     match cx.tcx.item_name(item.owner_id) {
                         sym::visit_str => seen_str = Some(cx.tcx.def_span(item.owner_id)),
                         sym::visit_string => seen_string = Some(cx.tcx.def_span(item.owner_id)),
diff --git a/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
index 9596b85664b..303f6028bd5 100644
--- a/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
@@ -48,10 +48,10 @@ declare_lint_pass!(ToStringTraitImpl => [TO_STRING_TRAIT_IMPL]);
 impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl {
     fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'tcx>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             ..
         }) = it.kind
-            && let Some(trait_did) = trait_ref.trait_def_id()
+            && let Some(trait_did) = of_trait.trait_ref.trait_def_id()
             && cx.tcx.is_diagnostic_item(sym::ToString, trait_did)
         {
             span_lint_and_help(
diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
index dcddff557d1..e843e169113 100644
--- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
@@ -137,9 +137,9 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt
         // We exclude `impl` blocks generated from rustc's proc macros.
         && !cx.tcx.is_automatically_derived(owner_id.to_def_id())
         // It is a implementation of a trait.
-        && let Some(trait_) = impl_.of_trait
+        && let Some(of_trait) = impl_.of_trait
     {
-        trait_.trait_def_id()
+        of_trait.trait_ref.trait_def_id()
     } else {
         None
     }
@@ -242,8 +242,8 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local
         // We exclude `impl` blocks generated from rustc's proc macros.
         && !cx.tcx.is_automatically_derived(owner_id.to_def_id())
         // It is a implementation of a trait.
-        && let Some(trait_) = impl_.of_trait
-        && let Some(trait_def_id) = trait_.trait_def_id()
+        && let Some(of_trait) = impl_.of_trait
+        && let Some(trait_def_id) = of_trait.trait_ref.trait_def_id()
         // The trait is `ToString`.
         && cx.tcx.is_diagnostic_item(sym::ToString, trait_def_id)
     {
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index cf603c6190b..1c52de52619 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -8,7 +8,7 @@ use clippy_utils::source::walk_span_to_context;
 use clippy_utils::visitors::{Descend, for_each_expr};
 use hir::HirId;
 use rustc_hir as hir;
-use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
+use rustc_hir::{Block, BlockCheckMode, Impl, ItemKind, Node, UnsafeSource};
 use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
@@ -204,7 +204,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
         let item_has_safety_comment = item_has_safety_comment(cx, item);
         match (&item.kind, item_has_safety_comment) {
             // lint unsafe impl without safety comment
-            (ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.safety.is_unsafe() => {
+            (ItemKind::Impl(Impl { of_trait: Some(of_trait), .. }), HasSafetyComment::No) if of_trait.safety.is_unsafe() => {
                 if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
                     && !is_unsafe_from_proc_macro(cx, item.span)
                 {
@@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
                 }
             },
             // lint safe impl with unnecessary safety comment
-            (ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.safety.is_safe() => {
+            (ItemKind::Impl(Impl { of_trait: Some(of_trait), .. }), HasSafetyComment::Yes(pos)) if of_trait.safety.is_safe() => {
                 if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
                     let (span, help_span) = mk_spans(pos);
 
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index d9a007635ca..a15e4e42e71 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -347,10 +347,10 @@ impl<'tcx> LateLintPass<'tcx> for Write {
 
 fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     if let ItemKind::Impl(Impl {
-        of_trait: Some(trait_ref),
+        of_trait: Some(of_trait),
         ..
     }) = &item.kind
-        && let Some(trait_id) = trait_ref.trait_def_id()
+        && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
     {
         cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
     } else {
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
index 0312bf56e59..24e017f7cf7 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
@@ -473,33 +473,27 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
             eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound)
         },
         (
-            Impl(box ast::Impl {
-                safety: lu,
-                polarity: lp,
-                defaultness: ld,
-                constness: lc,
+            Impl(ast::Impl {
                 generics: lg,
                 of_trait: lot,
                 self_ty: lst,
                 items: li,
             }),
-            Impl(box ast::Impl {
-                safety: ru,
-                polarity: rp,
-                defaultness: rd,
-                constness: rc,
+            Impl(ast::Impl {
                 generics: rg,
                 of_trait: rot,
                 self_ty: rst,
                 items: ri,
             }),
         ) => {
-            matches!(lu, Safety::Default) == matches!(ru, Safety::Default)
-                && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
-                && eq_defaultness(*ld, *rd)
-                && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
-                && eq_generics(lg, rg)
-                && both(lot.as_ref(), rot.as_ref(), |l, r| eq_path(&l.path, &r.path))
+            eq_generics(lg, rg)
+                && both(lot.as_deref(), rot.as_deref(), |l, r| {
+                    matches!(l.safety, Safety::Default) == matches!(r.safety, Safety::Default)
+                        && matches!(l.polarity, ImplPolarity::Positive) == matches!(r.polarity, ImplPolarity::Positive)
+                        && eq_defaultness(l.defaultness, r.defaultness)
+                        && matches!(l.constness, ast::Const::No) == matches!(r.constness, ast::Const::No)
+                        && eq_path(&l.trait_ref.path, &r.trait_ref.path)
+                })
                 && eq_ty(lst, rst)
                 && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
         },
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index dc31ed08fb7..e0c1b9d445a 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -19,8 +19,8 @@ use rustc_ast::token::CommentKind;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl,
-    ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety,
-    TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource,
+    ImplItem, ImplItemKind, TraitImplHeader, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path,
+    QPath, Safety, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource,
 };
 use rustc_lint::{EarlyContext, LateContext, LintContext};
 use rustc_middle::ty::TyCtxt;
@@ -254,7 +254,7 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
         ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")),
         ItemKind::Trait(_, _, Safety::Unsafe, ..)
         | ItemKind::Impl(Impl {
-            safety: Safety::Unsafe, ..
+            of_trait: Some(TraitImplHeader { safety: Safety::Unsafe, .. }), ..
         }) => (Pat::Str("unsafe"), Pat::Str("}")),
         ItemKind::Trait(_, IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")),
         ItemKind::Trait(..) => (Pat::Str("trait"), Pat::Str("}")),
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index fc716d86fc6..fcc120656e3 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -528,8 +528,9 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>
 pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
     if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
         && let ItemKind::Impl(impl_) = &item.kind
+        && let Some(of_trait) = impl_.of_trait
     {
-        return impl_.of_trait.as_ref();
+        return Some(&of_trait.trait_ref);
     }
     None
 }
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index ba5cbc73836..c9f5401ebe7 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -460,7 +460,8 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
         }
         fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
             if let ItemKind::Impl(i) = &self.cx.tcx.hir_item(id).kind
-                && i.safety.is_unsafe()
+                && let Some(of_trait) = i.of_trait
+                && of_trait.safety.is_unsafe()
             {
                 ControlFlow::Break(())
             } else {
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed
index 721d8b2c2dc..ec76abbef05 100644
--- a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed
@@ -1,7 +1,7 @@
 #![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)]
 #![warn(clippy::bool_assert_comparison)]
 
-use std::ops::Not;
+use std::ops::{Add, Not};
 
 macro_rules! a {
     () => {
@@ -62,6 +62,14 @@ impl Not for ImplNotTraitWithBool {
     }
 }
 
+impl Add for ImplNotTraitWithBool {
+    type Output = Self;
+
+    fn add(self, other: Self) -> Self::Output {
+        self
+    }
+}
+
 #[derive(Debug)]
 struct NonCopy;
 
@@ -94,7 +102,7 @@ fn main() {
     assert_eq!(a!(), "".is_empty());
     assert_eq!("".is_empty(), b!());
     assert_eq!(a, true);
-    assert!(b);
+    assert!(!!b);
     //~^ bool_assert_comparison
 
     assert_ne!("a".len(), 1);
@@ -122,7 +130,7 @@ fn main() {
     debug_assert_eq!(a!(), "".is_empty());
     debug_assert_eq!("".is_empty(), b!());
     debug_assert_eq!(a, true);
-    debug_assert!(b);
+    debug_assert!(!!b);
     //~^ bool_assert_comparison
 
     debug_assert_ne!("a".len(), 1);
@@ -167,7 +175,7 @@ fn main() {
 
     use debug_assert_eq as renamed;
     renamed!(a, true);
-    debug_assert!(b);
+    debug_assert!(!!b);
     //~^ bool_assert_comparison
 
     let non_copy = NonCopy;
@@ -199,4 +207,12 @@ fn main() {
     //~^ bool_assert_comparison
     debug_assert!(!"requires negation".is_empty());
     //~^ bool_assert_comparison
+    assert!(!b);
+    //~^ bool_assert_comparison
+    assert!(!(!b));
+    //~^ bool_assert_comparison
+    assert!(!!(b + b));
+    //~^ bool_assert_comparison
+    assert!(!(b + b));
+    //~^ bool_assert_comparison
 }
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
index 5ab4f475b06..40824a23c82 100644
--- a/src/tools/clippy/tests/ui/bool_assert_comparison.rs
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
@@ -1,7 +1,7 @@
 #![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)]
 #![warn(clippy::bool_assert_comparison)]
 
-use std::ops::Not;
+use std::ops::{Add, Not};
 
 macro_rules! a {
     () => {
@@ -62,6 +62,14 @@ impl Not for ImplNotTraitWithBool {
     }
 }
 
+impl Add for ImplNotTraitWithBool {
+    type Output = Self;
+
+    fn add(self, other: Self) -> Self::Output {
+        self
+    }
+}
+
 #[derive(Debug)]
 struct NonCopy;
 
@@ -199,4 +207,12 @@ fn main() {
     //~^ bool_assert_comparison
     debug_assert_eq!("requires negation".is_empty(), false);
     //~^ bool_assert_comparison
+    assert_eq!(!b, true);
+    //~^ bool_assert_comparison
+    assert_eq!(!b, false);
+    //~^ bool_assert_comparison
+    assert_eq!(b + b, true);
+    //~^ bool_assert_comparison
+    assert_eq!(b + b, false);
+    //~^ bool_assert_comparison
 }
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
index a1d0af54361..f823f08f31d 100644
--- a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
@@ -1,5 +1,5 @@
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:87:5
+  --> tests/ui/bool_assert_comparison.rs:95:5
    |
 LL |     assert_eq!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL +     assert!(!"a".is_empty());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:89:5
+  --> tests/ui/bool_assert_comparison.rs:97:5
    |
 LL |     assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL +     assert!("".is_empty());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:91:5
+  --> tests/ui/bool_assert_comparison.rs:99:5
    |
 LL |     assert_eq!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL +     assert!("".is_empty());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:97:5
+  --> tests/ui/bool_assert_comparison.rs:105:5
    |
 LL |     assert_eq!(b, true);
    |     ^^^^^^^^^^^^^^^^^^^
@@ -45,11 +45,11 @@ LL |     assert_eq!(b, true);
 help: replace it with `assert!(..)`
    |
 LL -     assert_eq!(b, true);
-LL +     assert!(b);
+LL +     assert!(!!b);
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:101:5
+  --> tests/ui/bool_assert_comparison.rs:109:5
    |
 LL |     assert_ne!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -61,7 +61,7 @@ LL +     assert!("a".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:103:5
+  --> tests/ui/bool_assert_comparison.rs:111:5
    |
 LL |     assert_ne!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -73,7 +73,7 @@ LL +     assert!(!"".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:105:5
+  --> tests/ui/bool_assert_comparison.rs:113:5
    |
 LL |     assert_ne!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -85,7 +85,7 @@ LL +     assert!(!"".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:111:5
+  --> tests/ui/bool_assert_comparison.rs:119:5
    |
 LL |     assert_ne!(b, true);
    |     ^^^^^^^^^^^^^^^^^^^
@@ -97,7 +97,7 @@ LL +     assert!(!b);
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:115:5
+  --> tests/ui/bool_assert_comparison.rs:123:5
    |
 LL |     debug_assert_eq!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -109,7 +109,7 @@ LL +     debug_assert!(!"a".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:117:5
+  --> tests/ui/bool_assert_comparison.rs:125:5
    |
 LL |     debug_assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -121,7 +121,7 @@ LL +     debug_assert!("".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:119:5
+  --> tests/ui/bool_assert_comparison.rs:127:5
    |
 LL |     debug_assert_eq!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -133,7 +133,7 @@ LL +     debug_assert!("".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:125:5
+  --> tests/ui/bool_assert_comparison.rs:133:5
    |
 LL |     debug_assert_eq!(b, true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -141,11 +141,11 @@ LL |     debug_assert_eq!(b, true);
 help: replace it with `debug_assert!(..)`
    |
 LL -     debug_assert_eq!(b, true);
-LL +     debug_assert!(b);
+LL +     debug_assert!(!!b);
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:129:5
+  --> tests/ui/bool_assert_comparison.rs:137:5
    |
 LL |     debug_assert_ne!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -157,7 +157,7 @@ LL +     debug_assert!("a".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:131:5
+  --> tests/ui/bool_assert_comparison.rs:139:5
    |
 LL |     debug_assert_ne!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -169,7 +169,7 @@ LL +     debug_assert!(!"".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:133:5
+  --> tests/ui/bool_assert_comparison.rs:141:5
    |
 LL |     debug_assert_ne!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -181,7 +181,7 @@ LL +     debug_assert!(!"".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:139:5
+  --> tests/ui/bool_assert_comparison.rs:147:5
    |
 LL |     debug_assert_ne!(b, true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -193,7 +193,7 @@ LL +     debug_assert!(!b);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:145:5
+  --> tests/ui/bool_assert_comparison.rs:153:5
    |
 LL |     assert_eq!("a".is_empty(), false, "tadam {}", 1);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -205,7 +205,7 @@ LL +     assert!(!"a".is_empty(), "tadam {}", 1);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:147:5
+  --> tests/ui/bool_assert_comparison.rs:155:5
    |
 LL |     assert_eq!("a".is_empty(), false, "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -217,7 +217,7 @@ LL +     assert!(!"a".is_empty(), "tadam {}", true);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:149:5
+  --> tests/ui/bool_assert_comparison.rs:157:5
    |
 LL |     assert_eq!(false, "a".is_empty(), "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -229,7 +229,7 @@ LL +     assert!(!"a".is_empty(), "tadam {}", true);
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:155:5
+  --> tests/ui/bool_assert_comparison.rs:163:5
    |
 LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -241,7 +241,7 @@ LL +     debug_assert!(!"a".is_empty(), "tadam {}", 1);
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:157:5
+  --> tests/ui/bool_assert_comparison.rs:165:5
    |
 LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -253,7 +253,7 @@ LL +     debug_assert!(!"a".is_empty(), "tadam {}", true);
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:159:5
+  --> tests/ui/bool_assert_comparison.rs:167:5
    |
 LL |     debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -265,31 +265,35 @@ LL +     debug_assert!(!"a".is_empty(), "tadam {}", true);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:163:5
+  --> tests/ui/bool_assert_comparison.rs:171:5
    |
 LL |     assert_eq!(a!(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^
    |
 help: replace it with `assert!(..)`
    |
-LL -     assert_eq!(a!(), true);
-LL +     assert!(a!());
+LL |         true
+...
+LL |
+LL ~     assert!(a!());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:165:5
+  --> tests/ui/bool_assert_comparison.rs:173:5
    |
 LL |     assert_eq!(true, b!());
    |     ^^^^^^^^^^^^^^^^^^^^^^
    |
 help: replace it with `assert!(..)`
    |
-LL -     assert_eq!(true, b!());
-LL +     assert!(b!());
+LL |         true
+...
+LL |
+LL ~     assert!(b!());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:170:5
+  --> tests/ui/bool_assert_comparison.rs:178:5
    |
 LL |     renamed!(b, true);
    |     ^^^^^^^^^^^^^^^^^
@@ -297,11 +301,11 @@ LL |     renamed!(b, true);
 help: replace it with `debug_assert!(..)`
    |
 LL -     renamed!(b, true);
-LL +     debug_assert!(b);
+LL +     debug_assert!(!!b);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:185:5
+  --> tests/ui/bool_assert_comparison.rs:193:5
    |
 LL |     assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -313,7 +317,7 @@ LL +     assert!("".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:187:5
+  --> tests/ui/bool_assert_comparison.rs:195:5
    |
 LL |     assert_ne!("".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -325,7 +329,7 @@ LL +     assert!("".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:189:5
+  --> tests/ui/bool_assert_comparison.rs:197:5
    |
 LL |     assert_ne!("requires negation".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -337,7 +341,7 @@ LL +     assert!(!"requires negation".is_empty());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:191:5
+  --> tests/ui/bool_assert_comparison.rs:199:5
    |
 LL |     assert_eq!("requires negation".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -349,7 +353,7 @@ LL +     assert!(!"requires negation".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:194:5
+  --> tests/ui/bool_assert_comparison.rs:202:5
    |
 LL |     debug_assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -361,7 +365,7 @@ LL +     debug_assert!("".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:196:5
+  --> tests/ui/bool_assert_comparison.rs:204:5
    |
 LL |     debug_assert_ne!("".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -373,7 +377,7 @@ LL +     debug_assert!("".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:198:5
+  --> tests/ui/bool_assert_comparison.rs:206:5
    |
 LL |     debug_assert_ne!("requires negation".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -385,7 +389,7 @@ LL +     debug_assert!(!"requires negation".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:200:5
+  --> tests/ui/bool_assert_comparison.rs:208:5
    |
 LL |     debug_assert_eq!("requires negation".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -396,5 +400,53 @@ LL -     debug_assert_eq!("requires negation".is_empty(), false);
 LL +     debug_assert!(!"requires negation".is_empty());
    |
 
-error: aborting due to 33 previous errors
+error: used `assert_eq!` with a literal bool
+  --> tests/ui/bool_assert_comparison.rs:210:5
+   |
+LL |     assert_eq!(!b, true);
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+help: replace it with `assert!(..)`
+   |
+LL -     assert_eq!(!b, true);
+LL +     assert!(!b);
+   |
+
+error: used `assert_eq!` with a literal bool
+  --> tests/ui/bool_assert_comparison.rs:212:5
+   |
+LL |     assert_eq!(!b, false);
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: replace it with `assert!(..)`
+   |
+LL -     assert_eq!(!b, false);
+LL +     assert!(!(!b));
+   |
+
+error: used `assert_eq!` with a literal bool
+  --> tests/ui/bool_assert_comparison.rs:214:5
+   |
+LL |     assert_eq!(b + b, true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: replace it with `assert!(..)`
+   |
+LL -     assert_eq!(b + b, true);
+LL +     assert!(!!(b + b));
+   |
+
+error: used `assert_eq!` with a literal bool
+  --> tests/ui/bool_assert_comparison.rs:216:5
+   |
+LL |     assert_eq!(b + b, false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: replace it with `assert!(..)`
+   |
+LL -     assert_eq!(b + b, false);
+LL +     assert!(!(b + b));
+   |
+
+error: aborting due to 37 previous errors
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.stderr b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
index f0778a4b32b..23f7300178e 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
@@ -3,6 +3,8 @@ error: malformed `path` attribute input
    |
 LL | #[path = foo!()]
    | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs
index ec6c2c60ca9..89bd93edae1 100644
--- a/src/tools/miri/src/borrow_tracker/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/mod.rs
@@ -260,7 +260,7 @@ impl GlobalStateInner {
         kind: MemoryKind,
         machine: &MiriMachine<'_>,
     ) -> AllocState {
-        let _span = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind);
+        let _trace = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind);
         match self.borrow_tracker_method {
             BorrowTrackerMethod::StackedBorrows =>
                 AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation(
@@ -281,7 +281,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         kind: RetagKind,
         val: &ImmTy<'tcx>,
     ) -> InterpResult<'tcx, ImmTy<'tcx>> {
-        let _span = enter_trace_span!(borrow_tracker::retag_ptr_value, ?kind, ?val.layout);
+        let _trace = enter_trace_span!(borrow_tracker::retag_ptr_value, ?kind, ?val.layout);
         let this = self.eval_context_mut();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -295,7 +295,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         kind: RetagKind,
         place: &PlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(borrow_tracker::retag_place_contents, ?kind, ?place);
+        let _trace = enter_trace_span!(borrow_tracker::retag_place_contents, ?kind, ?place);
         let this = self.eval_context_mut();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -305,7 +305,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
-        let _span = enter_trace_span!(borrow_tracker::protect_place, ?place);
+        let _trace = enter_trace_span!(borrow_tracker::protect_place, ?place);
         let this = self.eval_context_mut();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -315,7 +315,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
-        let _span =
+        let _trace =
             enter_trace_span!(borrow_tracker::expose_tag, alloc_id = alloc_id.0, tag = tag.0);
         let this = self.eval_context_ref();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
@@ -360,7 +360,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         &self,
         frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(borrow_tracker::on_stack_pop);
+        let _trace = enter_trace_span!(borrow_tracker::on_stack_pop);
         let this = self.eval_context_ref();
         let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap();
         // The body of this loop needs `borrow_tracker` immutably
@@ -438,7 +438,7 @@ impl AllocState {
         range: AllocRange,
         machine: &MiriMachine<'tcx>,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0);
+        let _trace = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0);
         match self {
             AllocState::StackedBorrows(sb) =>
                 sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine),
@@ -460,7 +460,7 @@ impl AllocState {
         range: AllocRange,
         machine: &MiriMachine<'tcx>,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0);
+        let _trace = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0);
         match self {
             AllocState::StackedBorrows(sb) =>
                 sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine),
@@ -482,7 +482,7 @@ impl AllocState {
         size: Size,
         machine: &MiriMachine<'tcx>,
     ) -> InterpResult<'tcx> {
-        let _span =
+        let _trace =
             enter_trace_span!(borrow_tracker::before_memory_deallocation, alloc_id = alloc_id.0);
         match self {
             AllocState::StackedBorrows(sb) =>
@@ -493,7 +493,7 @@ impl AllocState {
     }
 
     pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) {
-        let _span = enter_trace_span!(borrow_tracker::remove_unreachable_tags);
+        let _trace = enter_trace_span!(borrow_tracker::remove_unreachable_tags);
         match self {
             AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags),
             AllocState::TreeBorrows(tb) => tb.borrow_mut().remove_unreachable_tags(tags),
@@ -508,7 +508,7 @@ impl AllocState {
         tag: BorTag,
         alloc_id: AllocId, // diagnostics
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(
+        let _trace = enter_trace_span!(
             borrow_tracker::release_protector,
             alloc_id = alloc_id.0,
             tag = tag.0
@@ -523,7 +523,7 @@ impl AllocState {
 
 impl VisitProvenance for AllocState {
     fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
-        let _span = enter_trace_span!(borrow_tracker::visit_provenance);
+        let _trace = enter_trace_span!(borrow_tracker::visit_provenance);
         match self {
             AllocState::StackedBorrows(sb) => sb.visit_provenance(visit),
             AllocState::TreeBorrows(tb) => tb.visit_provenance(visit),
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index 57d4142ebe4..10df6f96702 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -958,20 +958,19 @@ fn format_impl_ref_and_type(
     offset: Indent,
 ) -> Option<String> {
     let ast::Impl {
-        safety,
-        polarity,
-        defaultness,
-        constness,
-        ref generics,
-        of_trait: ref trait_ref,
-        ref self_ty,
-        ..
-    } = *iimpl;
+        generics,
+        of_trait,
+        self_ty,
+        items: _,
+    } = iimpl;
     let mut result = String::with_capacity(128);
 
     result.push_str(&format_visibility(context, &item.vis));
-    result.push_str(format_defaultness(defaultness));
-    result.push_str(format_safety(safety));
+
+    if let Some(of_trait) = of_trait.as_deref() {
+        result.push_str(format_defaultness(of_trait.defaultness));
+        result.push_str(format_safety(of_trait.safety));
+    }
 
     let shape = if context.config.style_edition() >= StyleEdition::Edition2024 {
         Shape::indented(offset + last_line_width(&result), context.config)
@@ -984,28 +983,24 @@ fn format_impl_ref_and_type(
     };
     let generics_str = rewrite_generics(context, "impl", generics, shape).ok()?;
     result.push_str(&generics_str);
-    result.push_str(format_constness_right(constness));
 
-    let polarity_str = match polarity {
-        ast::ImplPolarity::Negative(_) => "!",
-        ast::ImplPolarity::Positive => "",
-    };
-
-    let polarity_overhead;
     let trait_ref_overhead;
-    if let Some(ref trait_ref) = *trait_ref {
+    if let Some(of_trait) = of_trait.as_deref() {
+        result.push_str(format_constness_right(of_trait.constness));
+        let polarity_str = match of_trait.polarity {
+            ast::ImplPolarity::Negative(_) => "!",
+            ast::ImplPolarity::Positive => "",
+        };
         let result_len = last_line_width(&result);
         result.push_str(&rewrite_trait_ref(
             context,
-            trait_ref,
+            &of_trait.trait_ref,
             offset,
             polarity_str,
             result_len,
         )?);
-        polarity_overhead = 0; // already written
         trait_ref_overhead = " for".len();
     } else {
-        polarity_overhead = polarity_str.len();
         trait_ref_overhead = 0;
     }
 
@@ -1020,17 +1015,15 @@ fn format_impl_ref_and_type(
     } else {
         0
     };
-    let used_space =
-        last_line_width(&result) + polarity_overhead + trait_ref_overhead + curly_brace_overhead;
+    let used_space = last_line_width(&result) + trait_ref_overhead + curly_brace_overhead;
     // 1 = space before the type.
     let budget = context.budget(used_space + 1);
     if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) {
         if !self_ty_str.contains('\n') {
-            if trait_ref.is_some() {
+            if of_trait.is_some() {
                 result.push_str(" for ");
             } else {
                 result.push(' ');
-                result.push_str(polarity_str);
             }
             result.push_str(&self_ty_str);
             return Some(result);
@@ -1042,12 +1035,10 @@ fn format_impl_ref_and_type(
     // Add indentation of one additional tab.
     let new_line_offset = offset.block_indent(context.config);
     result.push_str(&new_line_offset.to_string(context.config));
-    if trait_ref.is_some() {
+    if of_trait.is_some() {
         result.push_str("for ");
-    } else {
-        result.push_str(polarity_str);
     }
-    let budget = context.budget(last_line_width(&result) + polarity_overhead);
+    let budget = context.budget(last_line_width(&result));
     let type_offset = match context.config.indent_style() {
         IndentStyle::Visual => new_line_offset + trait_ref_overhead,
         IndentStyle::Block => new_line_offset,
diff --git a/src/tools/rustfmt/tests/source/negative-impl.rs b/src/tools/rustfmt/tests/source/negative-impl.rs
index da242d4f3dc..e8f9508e656 100644
--- a/src/tools/rustfmt/tests/source/negative-impl.rs
+++ b/src/tools/rustfmt/tests/source/negative-impl.rs
@@ -1,7 +1,3 @@
 impl ! Display for JoinHandle { }
 
-impl ! Box < JoinHandle > { }
-
 impl ! std :: fmt :: Display for JoinHandle < T : std :: future :: Future + std :: marker :: Send + std :: marker :: Sync > { }
-
-impl ! JoinHandle < T : std :: future :: Future < Output > + std :: marker :: Send + std :: marker :: Sync + 'static > + 'static { }
diff --git a/src/tools/rustfmt/tests/target/negative-impl.rs b/src/tools/rustfmt/tests/target/negative-impl.rs
index 16ce7e26a99..bb53048dbc6 100644
--- a/src/tools/rustfmt/tests/target/negative-impl.rs
+++ b/src/tools/rustfmt/tests/target/negative-impl.rs
@@ -1,14 +1,6 @@
 impl !Display for JoinHandle {}
 
-impl !Box<JoinHandle> {}
-
 impl !std::fmt::Display
     for JoinHandle<T: std::future::Future + std::marker::Send + std::marker::Sync>
 {
 }
-
-impl
-    !JoinHandle<T: std::future::Future<Output> + std::marker::Send + std::marker::Sync + 'static>
-        + 'static
-{
-}
diff --git a/tests/run-make/link-under-xcode/foo.rs b/tests/run-make/link-under-xcode/foo.rs
new file mode 100644
index 00000000000..f328e4d9d04
--- /dev/null
+++ b/tests/run-make/link-under-xcode/foo.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/tests/run-make/link-under-xcode/rmake.rs b/tests/run-make/link-under-xcode/rmake.rs
new file mode 100644
index 00000000000..c9394feb000
--- /dev/null
+++ b/tests/run-make/link-under-xcode/rmake.rs
@@ -0,0 +1,32 @@
+//! Test that linking works under an environment similar to what Xcode sets up.
+//!
+//! Regression test for https://github.com/rust-lang/rust/issues/80817.
+
+//@ only-apple
+
+use run_make_support::{cmd, rustc, target};
+
+fn main() {
+    // Fetch toolchain `/usr/bin` directory. Usually:
+    // /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
+    let clang_bin = cmd("xcrun").arg("--find").arg("clang").run().stdout_utf8();
+    let toolchain_bin = clang_bin.trim().strip_suffix("/clang").unwrap();
+
+    // Put toolchain directory at the front of PATH.
+    let path = format!("{}:{}", toolchain_bin, std::env::var("PATH").unwrap());
+
+    // Check that compiling and linking still works.
+    //
+    // Removing `SDKROOT` is necessary for the test to excercise what we want, since bootstrap runs
+    // under `/usr/bin/python3`, which will set SDKROOT for us.
+    rustc().target(target()).env_remove("SDKROOT").env("PATH", &path).input("foo.rs").run();
+
+    // Also check linking directly with the system linker.
+    rustc()
+        .target(target())
+        .env_remove("SDKROOT")
+        .env("PATH", &path)
+        .input("foo.rs")
+        .arg("-Clinker-flavor=ld")
+        .run();
+}
diff --git a/tests/rustdoc-ui/intra-doc/github-flavored-admonitions.rs b/tests/rustdoc-ui/intra-doc/github-flavored-admonitions.rs
new file mode 100644
index 00000000000..d5fa72eb993
--- /dev/null
+++ b/tests/rustdoc-ui/intra-doc/github-flavored-admonitions.rs
@@ -0,0 +1,6 @@
+// regression test for https://github.com/rust-lang/rust/issues/141866
+//@ check-pass
+#![deny(rustdoc::broken_intra_doc_links)]
+
+//! > [!NOTE]
+//! > This should not cause any warnings
diff --git a/tests/rustdoc/enum/enum-variant-value.rs b/tests/rustdoc/enum/enum-variant-value.rs
index 1670de8a24f..9cc85dfe10d 100644
--- a/tests/rustdoc/enum/enum-variant-value.rs
+++ b/tests/rustdoc/enum/enum-variant-value.rs
@@ -189,3 +189,25 @@ pub use bar::P;
 //@ has - '//*[@id="variant.A"]/h3' 'A(u32)'
 //@ matches - '//*[@id="variant.B"]/h3' '^B$'
 pub use bar::Q;
+
+// Ensure signed implicit discriminants are rendered correctly after a negative explicit value.
+//@ has 'foo/enum.R.html'
+//@ has - '//*[@class="rust item-decl"]/code' 'A = -2,'
+//@ has - '//*[@class="rust item-decl"]/code' 'B = -1,'
+//@ matches - '//*[@id="variant.A"]/h3' '^A = -2$'
+//@ matches - '//*[@id="variant.B"]/h3' '^B = -1$'
+pub enum R {
+    A = -2,
+    B,
+}
+
+// Also check that incrementing -1 yields 0 for the next implicit variant.
+//@ has 'foo/enum.S.html'
+//@ has - '//*[@class="rust item-decl"]/code' 'A = -1,'
+//@ has - '//*[@class="rust item-decl"]/code' 'B = 0,'
+//@ matches - '//*[@id="variant.A"]/h3' '^A = -1$'
+//@ matches - '//*[@id="variant.B"]/h3' '^B = 0$'
+pub enum S {
+    A = -1,
+    B,
+}
diff --git a/tests/ui/attributes/crate-name-macro-call.stderr b/tests/ui/attributes/crate-name-macro-call.stderr
index ab562b41a31..56827aa11a4 100644
--- a/tests/ui/attributes/crate-name-macro-call.stderr
+++ b/tests/ui/attributes/crate-name-macro-call.stderr
@@ -3,6 +3,8 @@ error: malformed `crate_name` attribute input
    |
 LL | #![crate_name = concat!("my", "crate")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/attributes/crate-type-delimited.stderr b/tests/ui/attributes/crate-type-delimited.stderr
index 0bbbe07b198..7f080f74838 100644
--- a/tests/ui/attributes/crate-type-delimited.stderr
+++ b/tests/ui/attributes/crate-type-delimited.stderr
@@ -2,7 +2,24 @@ error: malformed `crate_type` attribute input
   --> $DIR/crate-type-delimited.rs:2:1
    |
 LL | #![crate_type(lib)]
-   | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_type = "bin|lib|..."]`
+   | ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/linkage.html>
+help: the following are the possible correct uses
+   |
+LL - #![crate_type(lib)]
+LL + #![crate_type = "bin"]
+   |
+LL - #![crate_type(lib)]
+LL + #![crate_type = "cdylib"]
+   |
+LL - #![crate_type(lib)]
+LL + #![crate_type = "dylib"]
+   |
+LL - #![crate_type(lib)]
+LL + #![crate_type = "lib"]
+   |
+   = and 4 other candidates
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/attributes/crate-type-empty.stderr b/tests/ui/attributes/crate-type-empty.stderr
index b9279d961ee..f50bb33d6bb 100644
--- a/tests/ui/attributes/crate-type-empty.stderr
+++ b/tests/ui/attributes/crate-type-empty.stderr
@@ -2,7 +2,20 @@ error: malformed `crate_type` attribute input
   --> $DIR/crate-type-empty.rs:2:1
    |
 LL | #![crate_type]
-   | ^^^^^^^^^^^^^^ help: must be of the form: `#![crate_type = "bin|lib|..."]`
+   | ^^^^^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/linkage.html>
+help: the following are the possible correct uses
+   |
+LL | #![crate_type = "bin"]
+   |               +++++++
+LL | #![crate_type = "cdylib"]
+   |               ++++++++++
+LL | #![crate_type = "dylib"]
+   |               +++++++++
+LL | #![crate_type = "lib"]
+   |               +++++++
+   = and 4 other candidates
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/attributes/crate-type-macro-call.stderr b/tests/ui/attributes/crate-type-macro-call.stderr
index 6ccc3edf885..97938f7af24 100644
--- a/tests/ui/attributes/crate-type-macro-call.stderr
+++ b/tests/ui/attributes/crate-type-macro-call.stderr
@@ -2,7 +2,24 @@ error: malformed `crate_type` attribute input
   --> $DIR/crate-type-macro-call.rs:1:1
    |
 LL | #![crate_type = foo!()]
-   | ^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_type = "bin|lib|..."]`
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/linkage.html>
+help: the following are the possible correct uses
+   |
+LL - #![crate_type = foo!()]
+LL + #![crate_type = "bin"]
+   |
+LL - #![crate_type = foo!()]
+LL + #![crate_type = "cdylib"]
+   |
+LL - #![crate_type = foo!()]
+LL + #![crate_type = "dylib"]
+   |
+LL - #![crate_type = foo!()]
+LL + #![crate_type = "lib"]
+   |
+   = and 4 other candidates
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/attributes/invalid-macro-use.rs b/tests/ui/attributes/invalid-macro-use.rs
index cfb13fd183c..52e4608303f 100644
--- a/tests/ui/attributes/invalid-macro-use.rs
+++ b/tests/ui/attributes/invalid-macro-use.rs
@@ -8,21 +8,25 @@ extern crate std as s1;
 #[macro_use(5)]
 //~^ ERROR malformed `macro_use` attribute input
 //~| NOTE expected a valid identifier here
+//~| NOTE for more information, visit
 extern crate std as s2;
 
 #[macro_use(a = "b")]
 //~^ ERROR malformed `macro_use` attribute input
 //~| NOTE didn't expect any arguments here
+//~| NOTE for more information, visit
 extern crate std as s3;
 
 #[macro_use(a(b))]
 //~^ ERROR malformed `macro_use` attribute input
 //~| NOTE didn't expect any arguments here
+//~| NOTE for more information, visit
 extern crate std as s4;
 
 #[macro_use(a::b)]
 //~^ ERROR malformed `macro_use` attribute input
 //~| NOTE expected a valid identifier here
+//~| NOTE for more information, visit
 extern crate std as s5;
 
 #[macro_use(a)]
diff --git a/tests/ui/attributes/invalid-macro-use.stderr b/tests/ui/attributes/invalid-macro-use.stderr
index 4f5db5c558a..ff3ed6196d3 100644
--- a/tests/ui/attributes/invalid-macro-use.stderr
+++ b/tests/ui/attributes/invalid-macro-use.stderr
@@ -1,11 +1,11 @@
 error[E0469]: imported macro not found
-  --> $DIR/invalid-macro-use.rs:47:13
+  --> $DIR/invalid-macro-use.rs:51:13
    |
 LL | #[macro_use(a)]
    |             ^
 
 error[E0469]: imported macro not found
-  --> $DIR/invalid-macro-use.rs:49:13
+  --> $DIR/invalid-macro-use.rs:53:13
    |
 LL | #[macro_use(b)]
    |             ^
@@ -24,6 +24,7 @@ LL | #[macro_use(5)]
    |             |
    |             expected a valid identifier here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[macro_use(5)]
@@ -34,13 +35,14 @@ LL + #[macro_use]
    |
 
 error[E0565]: malformed `macro_use` attribute input
-  --> $DIR/invalid-macro-use.rs:13:1
+  --> $DIR/invalid-macro-use.rs:14:1
    |
 LL | #[macro_use(a = "b")]
    | ^^^^^^^^^^^^^^-----^^
    |               |
    |               didn't expect any arguments here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[macro_use(a = "b")]
@@ -51,13 +53,14 @@ LL + #[macro_use]
    |
 
 error[E0565]: malformed `macro_use` attribute input
-  --> $DIR/invalid-macro-use.rs:18:1
+  --> $DIR/invalid-macro-use.rs:20:1
    |
 LL | #[macro_use(a(b))]
    | ^^^^^^^^^^^^^---^^
    |              |
    |              didn't expect any arguments here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[macro_use(a(b))]
@@ -68,13 +71,14 @@ LL + #[macro_use]
    |
 
 error[E0539]: malformed `macro_use` attribute input
-  --> $DIR/invalid-macro-use.rs:23:1
+  --> $DIR/invalid-macro-use.rs:26:1
    |
 LL | #[macro_use(a::b)]
    | ^^^^^^^^^^^^----^^
    |             |
    |             expected a valid identifier here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[macro_use(a::b)]
@@ -85,13 +89,13 @@ LL + #[macro_use]
    |
 
 error: unused attribute
-  --> $DIR/invalid-macro-use.rs:28:1
+  --> $DIR/invalid-macro-use.rs:32:1
    |
 LL | #[macro_use(a)]
    | ^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/invalid-macro-use.rs:30:1
+  --> $DIR/invalid-macro-use.rs:34:1
    |
 LL | #[macro_use]
    | ^^^^^^^^^^^^
@@ -102,25 +106,25 @@ LL | #![deny(unused_attributes)]
    |         ^^^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/invalid-macro-use.rs:36:1
+  --> $DIR/invalid-macro-use.rs:40:1
    |
 LL | #[macro_use(a)]
    | ^^^^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/invalid-macro-use.rs:34:1
+  --> $DIR/invalid-macro-use.rs:38:1
    |
 LL | #[macro_use]
    | ^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/invalid-macro-use.rs:42:1
+  --> $DIR/invalid-macro-use.rs:46:1
    |
 LL | #[macro_use]
    | ^^^^^^^^^^^^ help: remove this attribute
    |
 note: attribute also specified here
-  --> $DIR/invalid-macro-use.rs:40:1
+  --> $DIR/invalid-macro-use.rs:44:1
    |
 LL | #[macro_use]
    | ^^^^^^^^^^^^
diff --git a/tests/ui/attributes/invalid-reprs.stderr b/tests/ui/attributes/invalid-reprs.stderr
index 415b969b244..72aaff92bd0 100644
--- a/tests/ui/attributes/invalid-reprs.stderr
+++ b/tests/ui/attributes/invalid-reprs.stderr
@@ -26,6 +26,7 @@ LL |     let y = #[repr(uwu(4))]
    |                    ^^^^^^
    |
    = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/attributes/lint_on_root.rs b/tests/ui/attributes/lint_on_root.rs
index 93d47bf0d71..9029da7dc97 100644
--- a/tests/ui/attributes/lint_on_root.rs
+++ b/tests/ui/attributes/lint_on_root.rs
@@ -1,7 +1,7 @@
 // NOTE: this used to panic in debug builds (by a sanity assertion)
 // and not emit any lint on release builds. See https://github.com/rust-lang/rust/issues/142891.
 #![inline = ""]
-//~^ ERROR valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+//~^ ERROR: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` [ill_formed_attribute_input]
 //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
 
 fn main() {}
diff --git a/tests/ui/attributes/lint_on_root.stderr b/tests/ui/attributes/lint_on_root.stderr
index aa0645b6194..91b72730530 100644
--- a/tests/ui/attributes/lint_on_root.stderr
+++ b/tests/ui/attributes/lint_on_root.stderr
@@ -1,4 +1,4 @@
-error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
   --> $DIR/lint_on_root.rs:3:1
    |
 LL | #![inline = ""]
@@ -11,7 +11,7 @@ LL | #![inline = ""]
 error: aborting due to 1 previous error
 
 Future incompatibility report: Future breakage diagnostic:
-error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
   --> $DIR/lint_on_root.rs:3:1
    |
 LL | #![inline = ""]
diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs
index 0d5bf69d548..3261b29fe7e 100644
--- a/tests/ui/attributes/malformed-attrs.rs
+++ b/tests/ui/attributes/malformed-attrs.rs
@@ -78,7 +78,7 @@
 #[export_stable = 1]
 //~^ ERROR malformed
 #[link]
-//~^ ERROR attribute must be of the form
+//~^ ERROR valid forms for the attribute are
 //~| WARN this was previously accepted by the compiler
 #[link_name]
 //~^ ERROR malformed
diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr
index dd9dd3a6ce7..705050e9a7d 100644
--- a/tests/ui/attributes/malformed-attrs.stderr
+++ b/tests/ui/attributes/malformed-attrs.stderr
@@ -6,6 +6,8 @@ LL | #[cfg]
    | |
    | expected this to be a list
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error: malformed `cfg_attr` attribute input
   --> $DIR/malformed-attrs.rs:101:1
@@ -29,13 +31,23 @@ error: malformed `windows_subsystem` attribute input
   --> $DIR/malformed-attrs.rs:26:1
    |
 LL | #![windows_subsystem]
-   | ^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![windows_subsystem = "windows|console"]`
+   | ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute>
+help: the following are the possible correct uses
+   |
+LL | #![windows_subsystem = "console"]
+   |                      +++++++++++
+LL | #![windows_subsystem = "windows"]
+   |                      +++++++++++
 
 error: malformed `crate_name` attribute input
   --> $DIR/malformed-attrs.rs:71:1
    |
 LL | #[crate_name]
    | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
 
 error: malformed `no_sanitize` attribute input
   --> $DIR/malformed-attrs.rs:89:1
@@ -48,6 +60,8 @@ error: malformed `instruction_set` attribute input
    |
 LL | #[instruction_set]
    | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[instruction_set(set)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute>
 
 error: malformed `patchable_function_entry` attribute input
   --> $DIR/malformed-attrs.rs:105:1
@@ -80,43 +94,108 @@ error: malformed `linkage` attribute input
   --> $DIR/malformed-attrs.rs:170:5
    |
 LL |     #[linkage]
-   |     ^^^^^^^^^^ help: must be of the form: `#[linkage = "external|internal|..."]`
+   |     ^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/linkage.html>
+help: the following are the possible correct uses
+   |
+LL |     #[linkage = "available_externally"]
+   |               ++++++++++++++++++++++++
+LL |     #[linkage = "common"]
+   |               ++++++++++
+LL |     #[linkage = "extern_weak"]
+   |               +++++++++++++++
+LL |     #[linkage = "external"]
+   |               ++++++++++++
+   = and 5 other candidates
 
 error: malformed `allow` attribute input
   --> $DIR/malformed-attrs.rs:175:1
    |
 LL | #[allow]
-   | ^^^^^^^^ help: must be of the form: `#[allow(lint1, lint2, ..., /*opt*/ reason = "...")]`
+   | ^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes>
+help: the following are the possible correct uses
+   |
+LL | #[allow(lint1)]
+   |        +++++++
+LL | #[allow(lint1, lint2, ...)]
+   |        +++++++++++++++++++
+LL | #[allow(lint1, lint2, lint3, reason = "...")]
+   |        +++++++++++++++++++++++++++++++++++++
 
 error: malformed `expect` attribute input
   --> $DIR/malformed-attrs.rs:177:1
    |
 LL | #[expect]
-   | ^^^^^^^^^ help: must be of the form: `#[expect(lint1, lint2, ..., /*opt*/ reason = "...")]`
+   | ^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes>
+help: the following are the possible correct uses
+   |
+LL | #[expect(lint1)]
+   |         +++++++
+LL | #[expect(lint1, lint2, ...)]
+   |         +++++++++++++++++++
+LL | #[expect(lint1, lint2, lint3, reason = "...")]
+   |         +++++++++++++++++++++++++++++++++++++
 
 error: malformed `warn` attribute input
   --> $DIR/malformed-attrs.rs:179:1
    |
 LL | #[warn]
-   | ^^^^^^^ help: must be of the form: `#[warn(lint1, lint2, ..., /*opt*/ reason = "...")]`
+   | ^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes>
+help: the following are the possible correct uses
+   |
+LL | #[warn(lint1)]
+   |       +++++++
+LL | #[warn(lint1, lint2, ...)]
+   |       +++++++++++++++++++
+LL | #[warn(lint1, lint2, lint3, reason = "...")]
+   |       +++++++++++++++++++++++++++++++++++++
 
 error: malformed `deny` attribute input
   --> $DIR/malformed-attrs.rs:181:1
    |
 LL | #[deny]
-   | ^^^^^^^ help: must be of the form: `#[deny(lint1, lint2, ..., /*opt*/ reason = "...")]`
+   | ^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes>
+help: the following are the possible correct uses
+   |
+LL | #[deny(lint1)]
+   |       +++++++
+LL | #[deny(lint1, lint2, ...)]
+   |       +++++++++++++++++++
+LL | #[deny(lint1, lint2, lint3, reason = "...")]
+   |       +++++++++++++++++++++++++++++++++++++
 
 error: malformed `forbid` attribute input
   --> $DIR/malformed-attrs.rs:183:1
    |
 LL | #[forbid]
-   | ^^^^^^^^^ help: must be of the form: `#[forbid(lint1, lint2, ..., /*opt*/ reason = "...")]`
+   | ^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes>
+help: the following are the possible correct uses
+   |
+LL | #[forbid(lint1)]
+   |         +++++++
+LL | #[forbid(lint1, lint2, ...)]
+   |         +++++++++++++++++++
+LL | #[forbid(lint1, lint2, lint3, reason = "...")]
+   |         +++++++++++++++++++++++++++++++++++++
 
 error: malformed `debugger_visualizer` attribute input
   --> $DIR/malformed-attrs.rs:185:1
    |
 LL | #[debugger_visualizer]
    | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[debugger_visualizer(natvis_file = "...", gdb_script_file = "...")]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute>
 
 error: malformed `thread_local` attribute input
   --> $DIR/malformed-attrs.rs:200:1
@@ -129,6 +208,8 @@ error: malformed `no_link` attribute input
    |
 LL | #[no_link()]
    | ^^^^^^^^^^^^ help: must be of the form: `#[no_link]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/extern-crates.html#the-no_link-attribute>
 
 error: malformed `macro_export` attribute input
   --> $DIR/malformed-attrs.rs:211:1
@@ -136,6 +217,7 @@ error: malformed `macro_export` attribute input
 LL | #[macro_export = 18]
    | ^^^^^^^^^^^^^^^^^^^^
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope>
 help: the following are the possible correct uses
    |
 LL - #[macro_export = 18]
@@ -145,12 +227,6 @@ LL - #[macro_export = 18]
 LL + #[macro_export]
    |
 
-error: malformed `allow_internal_unsafe` attribute input
-  --> $DIR/malformed-attrs.rs:213:1
-   |
-LL | #[allow_internal_unsafe = 1]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[allow_internal_unsafe]`
-
 error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type
   --> $DIR/malformed-attrs.rs:96:1
    |
@@ -178,7 +254,7 @@ LL | #[allow_internal_unsafe = 1]
    = help: add `#![feature(allow_internal_unsafe)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]`
+error: valid forms for the attribute are `#[doc(hidden)]`, `#[doc(inline)]`, and `#[doc = "string"]`
   --> $DIR/malformed-attrs.rs:40:1
    |
 LL | #[doc]
@@ -186,9 +262,10 @@ LL | #[doc]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
-error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]`
+error: valid forms for the attribute are `#[doc(hidden)]`, `#[doc(inline)]`, and `#[doc = "string"]`
   --> $DIR/malformed-attrs.rs:73:1
    |
 LL | #[doc]
@@ -196,8 +273,9 @@ LL | #[doc]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html>
 
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/malformed-attrs.rs:80:1
    |
 LL | #[link]
@@ -205,6 +283,7 @@ LL | #[link]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
 
 error: invalid argument
   --> $DIR/malformed-attrs.rs:185:1
@@ -257,26 +336,49 @@ LL - #[deprecated = 5]
 LL + #[deprecated = "reason"]
    |
 LL - #[deprecated = 5]
-LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+LL + #[deprecated(note = "reason")]
    |
 LL - #[deprecated = 5]
-LL + #[deprecated]
+LL + #[deprecated(since = "version")]
    |
+LL - #[deprecated = 5]
+LL + #[deprecated(since = "version", note = "reason")]
+   |
+   = and 1 other candidate
 
 error[E0539]: malformed `rustc_macro_transparency` attribute input
   --> $DIR/malformed-attrs.rs:43:1
    |
 LL | #[rustc_macro_transparency]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_macro_transparency = "transparent|semitransparent|opaque"]`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL | #[rustc_macro_transparency = "opaque"]
+   |                            ++++++++++
+LL | #[rustc_macro_transparency = "semitransparent"]
+   |                            +++++++++++++++++++
+LL | #[rustc_macro_transparency = "transparent"]
+   |                            +++++++++++++++
 
 error[E0539]: malformed `repr` attribute input
   --> $DIR/malformed-attrs.rs:45:1
    |
 LL | #[repr]
-   | ^^^^^^^
-   | |
-   | expected this to be a list
-   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
+   | ^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL | #[repr(<integer type>)]
+   |       ++++++++++++++++
+LL | #[repr(C)]
+   |       +++
+LL | #[repr(Rust)]
+   |       ++++++
+LL | #[repr(align(...))]
+   |       ++++++++++++
+   = and 2 other candidates
 
 error[E0565]: malformed `rustc_as_ptr` attribute input
   --> $DIR/malformed-attrs.rs:48:1
@@ -300,10 +402,16 @@ error[E0539]: malformed `optimize` attribute input
   --> $DIR/malformed-attrs.rs:55:1
    |
 LL | #[optimize]
-   | ^^^^^^^^^^^
-   | |
-   | expected this to be a list
-   | help: must be of the form: `#[optimize(size|speed|none)]`
+   | ^^^^^^^^^^^ expected this to be a list
+   |
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL | #[optimize(none)]
+   |           ++++++
+LL | #[optimize(size)]
+   |           ++++++
+LL | #[optimize(speed)]
+   |           +++++++
 
 error[E0565]: malformed `cold` attribute input
   --> $DIR/malformed-attrs.rs:57:1
@@ -363,8 +471,10 @@ LL | #[used()]
    |
 help: try changing it to one of the following valid forms of the attribute
    |
-LL | #[used(compiler|linker)]
-   |        +++++++++++++++
+LL | #[used(compiler)]
+   |        ++++++++
+LL | #[used(linker)]
+   |        ++++++
 LL - #[used()]
 LL + #[used]
    |
@@ -392,12 +502,16 @@ error[E0539]: malformed `link_name` attribute input
    |
 LL | #[link_name]
    | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute>
 
 error[E0539]: malformed `link_section` attribute input
   --> $DIR/malformed-attrs.rs:85:1
    |
 LL | #[link_section]
    | ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute>
 
 error[E0539]: malformed `coverage` attribute input
   --> $DIR/malformed-attrs.rs:87:1
@@ -456,6 +570,7 @@ LL | #[must_use = 1]
    |              |
    |              expected a string literal here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[must_use = 1]
@@ -469,10 +584,15 @@ error[E0539]: malformed `proc_macro_derive` attribute input
   --> $DIR/malformed-attrs.rs:120:1
    |
 LL | #[proc_macro_derive]
-   | ^^^^^^^^^^^^^^^^^^^^
-   | |
-   | expected this to be a list
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   | ^^^^^^^^^^^^^^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL | #[proc_macro_derive(TraitName)]
+   |                    +++++++++++
+LL | #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |                    ++++++++++++++++++++++++++++++++++++++++++
 
 error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input
   --> $DIR/malformed-attrs.rs:125:1
@@ -527,6 +647,8 @@ LL |     #[link_ordinal]
    |     |
    |     expected this to be a list
    |     help: must be of the form: `#[link_ordinal(ordinal)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute>
 
 error[E0565]: malformed `ffi_const` attribute input
   --> $DIR/malformed-attrs.rs:168:5
@@ -561,6 +683,15 @@ error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `
 LL | #[macro_use = 1]
    | ^^^^^^^^^^^^^^^^
 
+error[E0565]: malformed `allow_internal_unsafe` attribute input
+  --> $DIR/malformed-attrs.rs:213:1
+   |
+LL | #[allow_internal_unsafe = 1]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^---^
+   | |                       |
+   | |                       didn't expect any arguments here
+   | help: must be of the form: `#[allow_internal_unsafe]`
+
 error[E0565]: malformed `type_const` attribute input
   --> $DIR/malformed-attrs.rs:140:5
    |
@@ -618,7 +749,7 @@ LL | #[diagnostic::on_unimplemented = 1]
    |
    = help: only `message`, `note` and `label` are allowed as options
 
-error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
   --> $DIR/malformed-attrs.rs:50:1
    |
 LL | #[inline = 5]
@@ -661,7 +792,7 @@ error: aborting due to 74 previous errors; 3 warnings emitted
 Some errors have detailed explanations: E0308, E0463, E0539, E0565, E0658, E0805.
 For more information about an error, try `rustc --explain E0308`.
 Future incompatibility report: Future breakage diagnostic:
-error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]`
+error: valid forms for the attribute are `#[doc(hidden)]`, `#[doc(inline)]`, and `#[doc = "string"]`
   --> $DIR/malformed-attrs.rs:40:1
    |
 LL | #[doc]
@@ -669,10 +800,11 @@ LL | #[doc]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 Future breakage diagnostic:
-error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]`
+error: valid forms for the attribute are `#[doc(hidden)]`, `#[doc(inline)]`, and `#[doc = "string"]`
   --> $DIR/malformed-attrs.rs:73:1
    |
 LL | #[doc]
@@ -680,10 +812,11 @@ LL | #[doc]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 Future breakage diagnostic:
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/malformed-attrs.rs:80:1
    |
 LL | #[link]
@@ -691,10 +824,11 @@ LL | #[link]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 Future breakage diagnostic:
-error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
   --> $DIR/malformed-attrs.rs:50:1
    |
 LL | #[inline = 5]
diff --git a/tests/ui/attributes/malformed-reprs.stderr b/tests/ui/attributes/malformed-reprs.stderr
index c39c98dde31..43085b9c341 100644
--- a/tests/ui/attributes/malformed-reprs.stderr
+++ b/tests/ui/attributes/malformed-reprs.stderr
@@ -2,10 +2,24 @@ error[E0539]: malformed `repr` attribute input
   --> $DIR/malformed-reprs.rs:4:1
    |
 LL | #![repr]
-   | ^^^^^^^^
-   | |
-   | expected this to be a list
-   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
+   | ^^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #![repr]
+LL + #[repr(<integer type>)]
+   |
+LL - #![repr]
+LL + #[repr(C)]
+   |
+LL - #![repr]
+LL + #[repr(Rust)]
+   |
+LL - #![repr]
+LL + #[repr(align(...))]
+   |
+   = and 2 other candidates
 
 error[E0589]: invalid `repr(align)` attribute: not a power of two
   --> $DIR/malformed-reprs.rs:9:14
diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
index 884e7663c85..107e053ae0c 100644
--- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
+++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr
@@ -119,9 +119,18 @@ error[E0565]: malformed `proc_macro_derive` attribute input
    |
 LL | #[proc_macro_derive(unsafe(Foo))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^-----^^
-   | |                         |
-   | |                         didn't expect any arguments here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                           |
+   |                           didn't expect any arguments here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(unsafe(Foo))]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(unsafe(Foo))]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0452]: malformed lint attribute input
   --> $DIR/proc-unsafe-attributes.rs:27:16
diff --git a/tests/ui/attributes/used_with_multi_args.stderr b/tests/ui/attributes/used_with_multi_args.stderr
index e48209cf204..308f0519b8c 100644
--- a/tests/ui/attributes/used_with_multi_args.stderr
+++ b/tests/ui/attributes/used_with_multi_args.stderr
@@ -9,7 +9,10 @@ LL | #[used(compiler, linker)]
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[used(compiler, linker)]
-LL + #[used(compiler|linker)]
+LL + #[used(compiler)]
+   |
+LL - #[used(compiler, linker)]
+LL + #[used(linker)]
    |
 LL - #[used(compiler, linker)]
 LL + #[used]
diff --git a/tests/ui/cfg/cfg-target-compact-errors.stderr b/tests/ui/cfg/cfg-target-compact-errors.stderr
index 7df6729e881..cf61f94278a 100644
--- a/tests/ui/cfg/cfg-target-compact-errors.stderr
+++ b/tests/ui/cfg/cfg-target-compact-errors.stderr
@@ -6,6 +6,8 @@ LL | #[cfg(target(o::o))]
    | |            |
    | |            expected this to be of the form `... = "..."`
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error[E0539]: malformed `cfg` attribute input
   --> $DIR/cfg-target-compact-errors.rs:9:1
@@ -15,6 +17,8 @@ LL | #[cfg(target(os = 8))]
    | |                 |
    | |                 expected a string literal here
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error[E0539]: malformed `cfg` attribute input
   --> $DIR/cfg-target-compact-errors.rs:13:1
@@ -24,6 +28,8 @@ LL | #[cfg(target(os = "linux", pointer(width = "64")))]
    | |                          |
    | |                          expected this to be of the form `... = "..."`
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error[E0539]: malformed `cfg` attribute input
   --> $DIR/cfg-target-compact-errors.rs:17:1
@@ -33,6 +39,8 @@ LL | #[cfg(target(true))]
    | |            |
    | |            expected this to be of the form `... = "..."`
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error: `cfg` predicate key must be an identifier
   --> $DIR/cfg-target-compact-errors.rs:21:14
diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs
index df87a3d846e..126a534dc6f 100644
--- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs
+++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.rs
@@ -1,21 +1,25 @@
 #[cfg]
 //~^ ERROR malformed `cfg` attribute
 //~| NOTE expected this to be a list
+//~| NOTE for more information, visit
 struct S1;
 
 #[cfg = 10]
 //~^ ERROR malformed `cfg` attribute
 //~| NOTE expected this to be a list
+//~| NOTE for more information, visit
 struct S2;
 
 #[cfg()]
 //~^ ERROR malformed `cfg` attribute
 //~| NOTE expected a single argument here
+//~| NOTE for more information, visit
 struct S3;
 
 #[cfg(a, b)]
 //~^ ERROR malformed `cfg` attribute
 //~| NOTE expected a single argument here
+//~| NOTE for more information, visit
 struct S4;
 
 #[cfg("str")] //~ ERROR `cfg` predicate key must be an identifier
@@ -29,6 +33,7 @@ struct S7;
 
 #[cfg(a = 10)] //~ ERROR malformed `cfg` attribute input
 //~^ NOTE expected a string literal here
+//~| NOTE for more information, visit
 struct S8;
 
 #[cfg(a = b"hi")]  //~ ERROR malformed `cfg` attribute input
diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr
index 75e9b9209c0..6acde758ea5 100644
--- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr
+++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr
@@ -6,63 +6,73 @@ LL | #[cfg]
    | |
    | expected this to be a list
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error[E0539]: malformed `cfg` attribute input
-  --> $DIR/cfg-attr-syntax-validation.rs:6:1
+  --> $DIR/cfg-attr-syntax-validation.rs:7:1
    |
 LL | #[cfg = 10]
    | ^^^^^^^^^^^
    | |
    | expected this to be a list
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error[E0805]: malformed `cfg` attribute input
-  --> $DIR/cfg-attr-syntax-validation.rs:11:1
+  --> $DIR/cfg-attr-syntax-validation.rs:13:1
    |
 LL | #[cfg()]
    | ^^^^^--^
    | |    |
    | |    expected a single argument here
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error[E0805]: malformed `cfg` attribute input
-  --> $DIR/cfg-attr-syntax-validation.rs:16:1
+  --> $DIR/cfg-attr-syntax-validation.rs:19:1
    |
 LL | #[cfg(a, b)]
    | ^^^^^------^
    | |    |
    | |    expected a single argument here
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error: `cfg` predicate key must be an identifier
-  --> $DIR/cfg-attr-syntax-validation.rs:21:7
+  --> $DIR/cfg-attr-syntax-validation.rs:25:7
    |
 LL | #[cfg("str")]
    |       ^^^^^
 
 error: `cfg` predicate key must be an identifier
-  --> $DIR/cfg-attr-syntax-validation.rs:24:7
+  --> $DIR/cfg-attr-syntax-validation.rs:28:7
    |
 LL | #[cfg(a::b)]
    |       ^^^^
 
 error[E0537]: invalid predicate `a`
-  --> $DIR/cfg-attr-syntax-validation.rs:27:7
+  --> $DIR/cfg-attr-syntax-validation.rs:31:7
    |
 LL | #[cfg(a())]
    |       ^^^
 
 error[E0539]: malformed `cfg` attribute input
-  --> $DIR/cfg-attr-syntax-validation.rs:30:1
+  --> $DIR/cfg-attr-syntax-validation.rs:34:1
    |
 LL | #[cfg(a = 10)]
    | ^^^^^^^^^^--^^
    | |         |
    | |         expected a string literal here
    | help: must be of the form: `#[cfg(predicate)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute>
 
 error[E0539]: malformed `cfg` attribute input
-  --> $DIR/cfg-attr-syntax-validation.rs:34:1
+  --> $DIR/cfg-attr-syntax-validation.rs:39:1
    |
 LL | #[cfg(a = b"hi")]
    | ^^^^^^^^^^-^^^^^^
@@ -72,7 +82,7 @@ LL | #[cfg(a = b"hi")]
    = note: expected a normal string literal, not a byte string literal
 
 error: expected unsuffixed literal, found `expr` metavariable
-  --> $DIR/cfg-attr-syntax-validation.rs:40:25
+  --> $DIR/cfg-attr-syntax-validation.rs:45:25
    |
 LL |         #[cfg(feature = $expr)]
    |                         ^^^^^
diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr
index f1b4697485c..856f51a4b24 100644
--- a/tests/ui/deprecation/deprecation-sanity.stderr
+++ b/tests/ui/deprecation/deprecation-sanity.stderr
@@ -18,11 +18,15 @@ LL -     #[deprecated(since = "a", note)]
 LL +     #[deprecated = "reason"]
    |
 LL -     #[deprecated(since = "a", note)]
-LL +     #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+LL +     #[deprecated(note = "reason")]
    |
 LL -     #[deprecated(since = "a", note)]
-LL +     #[deprecated]
+LL +     #[deprecated(since = "version")]
    |
+LL -     #[deprecated(since = "a", note)]
+LL +     #[deprecated(since = "version", note = "reason")]
+   |
+   = and 1 other candidate
 
 error[E0539]: malformed `deprecated` attribute input
   --> $DIR/deprecation-sanity.rs:10:5
@@ -38,11 +42,15 @@ LL -     #[deprecated(since, note = "a")]
 LL +     #[deprecated = "reason"]
    |
 LL -     #[deprecated(since, note = "a")]
-LL +     #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+LL +     #[deprecated(note = "reason")]
+   |
+LL -     #[deprecated(since, note = "a")]
+LL +     #[deprecated(since = "version")]
    |
 LL -     #[deprecated(since, note = "a")]
-LL +     #[deprecated]
+LL +     #[deprecated(since = "version", note = "reason")]
    |
+   = and 1 other candidate
 
 error[E0539]: malformed `deprecated` attribute input
   --> $DIR/deprecation-sanity.rs:13:5
@@ -58,11 +66,15 @@ LL -     #[deprecated(since = "a", note(b))]
 LL +     #[deprecated = "reason"]
    |
 LL -     #[deprecated(since = "a", note(b))]
-LL +     #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+LL +     #[deprecated(note = "reason")]
+   |
+LL -     #[deprecated(since = "a", note(b))]
+LL +     #[deprecated(since = "version")]
    |
 LL -     #[deprecated(since = "a", note(b))]
-LL +     #[deprecated]
+LL +     #[deprecated(since = "version", note = "reason")]
    |
+   = and 1 other candidate
 
 error[E0539]: malformed `deprecated` attribute input
   --> $DIR/deprecation-sanity.rs:16:5
@@ -78,11 +90,15 @@ LL -     #[deprecated(since(b), note = "a")]
 LL +     #[deprecated = "reason"]
    |
 LL -     #[deprecated(since(b), note = "a")]
-LL +     #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+LL +     #[deprecated(note = "reason")]
    |
 LL -     #[deprecated(since(b), note = "a")]
-LL +     #[deprecated]
+LL +     #[deprecated(since = "version")]
    |
+LL -     #[deprecated(since(b), note = "a")]
+LL +     #[deprecated(since = "version", note = "reason")]
+   |
+   = and 1 other candidate
 
 error[E0539]: malformed `deprecated` attribute input
   --> $DIR/deprecation-sanity.rs:19:5
@@ -108,11 +124,15 @@ LL -     #[deprecated("test")]
 LL +     #[deprecated = "reason"]
    |
 LL -     #[deprecated("test")]
-LL +     #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+LL +     #[deprecated(note = "reason")]
+   |
+LL -     #[deprecated("test")]
+LL +     #[deprecated(since = "version")]
    |
 LL -     #[deprecated("test")]
-LL +     #[deprecated]
+LL +     #[deprecated(since = "version", note = "reason")]
    |
+   = and 1 other candidate
 
 error: multiple `deprecated` attributes
   --> $DIR/deprecation-sanity.rs:27:1
@@ -140,11 +160,15 @@ LL - #[deprecated(since = "a", since = "b", note = "c")]
 LL + #[deprecated = "reason"]
    |
 LL - #[deprecated(since = "a", since = "b", note = "c")]
-LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+LL + #[deprecated(note = "reason")]
+   |
+LL - #[deprecated(since = "a", since = "b", note = "c")]
+LL + #[deprecated(since = "version")]
    |
 LL - #[deprecated(since = "a", since = "b", note = "c")]
-LL + #[deprecated]
+LL + #[deprecated(since = "version", note = "reason")]
    |
+   = and 1 other candidate
 
 error: this `#[deprecated]` annotation has no effect
   --> $DIR/deprecation-sanity.rs:35:1
diff --git a/tests/ui/error-codes/E0197.stderr b/tests/ui/error-codes/E0197.stderr
index c06777396e8..04c6174b9f1 100644
--- a/tests/ui/error-codes/E0197.stderr
+++ b/tests/ui/error-codes/E0197.stderr
@@ -5,6 +5,8 @@ LL | unsafe impl Foo { }
    | ------      ^^^ inherent impl for this type
    | |
    | unsafe because of this
+   |
+   = note: only trait implementations may be annotated with `unsafe`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/error-codes/E0540.stderr b/tests/ui/error-codes/E0540.stderr
index 3e5f408feb5..ae23dce5c50 100644
--- a/tests/ui/error-codes/E0540.stderr
+++ b/tests/ui/error-codes/E0540.stderr
@@ -6,10 +6,13 @@ LL | #[inline()]
    |         |
    |         expected a single argument here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
-LL | #[inline(always|never)]
-   |          ++++++++++++
+LL | #[inline(always)]
+   |          ++++++
+LL | #[inline(never)]
+   |          +++++
 LL - #[inline()]
 LL + #[inline]
    |
diff --git a/tests/ui/error-codes/E0565-1.stderr b/tests/ui/error-codes/E0565-1.stderr
index 6277e6400d7..52daf2a62fc 100644
--- a/tests/ui/error-codes/E0565-1.stderr
+++ b/tests/ui/error-codes/E0565-1.stderr
@@ -12,11 +12,15 @@ LL - #[deprecated("since")]
 LL + #[deprecated = "reason"]
    |
 LL - #[deprecated("since")]
-LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+LL + #[deprecated(note = "reason")]
    |
 LL - #[deprecated("since")]
-LL + #[deprecated]
+LL + #[deprecated(since = "version")]
    |
+LL - #[deprecated("since")]
+LL + #[deprecated(since = "version", note = "reason")]
+   |
+   = and 1 other candidate
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/extern/issue-47725.rs b/tests/ui/extern/issue-47725.rs
index 60d0cd62347..8ac866dc7d9 100644
--- a/tests/ui/extern/issue-47725.rs
+++ b/tests/ui/extern/issue-47725.rs
@@ -17,6 +17,7 @@ extern "C" {
 #[link_name]
 //~^ ERROR malformed `link_name` attribute input
 //~| HELP must be of the form
+//~| NOTE for more information, visit
 extern "C" {
     fn bar() -> u32;
 }
diff --git a/tests/ui/extern/issue-47725.stderr b/tests/ui/extern/issue-47725.stderr
index 4fd02a1778b..c5af54b8029 100644
--- a/tests/ui/extern/issue-47725.stderr
+++ b/tests/ui/extern/issue-47725.stderr
@@ -3,6 +3,8 @@ error[E0539]: malformed `link_name` attribute input
    |
 LL | #[link_name]
    | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute>
 
 warning: attribute should be applied to a foreign function or static
   --> $DIR/issue-47725.rs:3:1
diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
index e7e62b4f989..646abf8e4a1 100644
--- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
+++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr
@@ -43,9 +43,20 @@ error[E0539]: malformed `optimize` attribute input
    |
 LL | #[optimize(banana)]
    | ^^^^^^^^^^^------^^
-   | |          |
-   | |          valid arguments are `size`, `speed` or `none`
-   | help: must be of the form: `#[optimize(size|speed|none)]`
+   |            |
+   |            valid arguments are `size`, `speed` or `none`
+   |
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[optimize(banana)]
+LL + #[optimize(none)]
+   |
+LL - #[optimize(banana)]
+LL + #[optimize(size)]
+   |
+LL - #[optimize(banana)]
+LL + #[optimize(speed)]
+   |
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr
index 324f7e9fd8a..7550e26f4a7 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr
@@ -304,7 +304,7 @@ error[E0517]: attribute should be applied to a struct, enum, or union
 LL |     #[repr(Rust)] impl S { }
    |            ^^^^   ---------- not a struct, enum, or union
 
-error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
   --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5
    |
 LL |     #[inline = "2100"] fn f() { }
@@ -319,7 +319,7 @@ error: aborting due to 38 previous errors
 Some errors have detailed explanations: E0517, E0518, E0658.
 For more information about an error, try `rustc --explain E0517`.
 Future incompatibility report: Future breakage diagnostic:
-error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
   --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5
    |
 LL |     #[inline = "2100"] fn f() { }
diff --git a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr
index 6bf09a2b131..b773f7c97aa 100644
--- a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr
+++ b/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr
@@ -3,6 +3,8 @@ error: malformed `crate_name` attribute input
    |
 LL | #![crate_name = concat!("wrapped")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr
index de62aed79fc..64e38ed8533 100644
--- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr
+++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr
@@ -3,6 +3,8 @@ error: malformed `crate_name` attribute input
    |
 LL | #![crate_name]
    | ^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr
index 42c33de1221..e9a5b58e4f9 100644
--- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr
+++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr
@@ -3,6 +3,8 @@ error: malformed `crate_name` attribute input
    |
 LL | #![crate_name = concat!("this_one_is_not")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr
index cb5ffaab9ca..e63525c6a5d 100644
--- a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr
+++ b/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr
@@ -3,6 +3,8 @@ error: malformed `crate_name` attribute input
    |
 LL | #![crate_name = concat!("wrapped")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![crate_name = "name"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/invalid/invalid-inline.stderr b/tests/ui/invalid/invalid-inline.stderr
index 54e6b2b5408..78ffe3270a7 100644
--- a/tests/ui/invalid/invalid-inline.stderr
+++ b/tests/ui/invalid/invalid-inline.stderr
@@ -6,10 +6,14 @@ LL | #[inline(please,no)]
    |         |
    |         expected a single argument here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[inline(please,no)]
-LL + #[inline(always|never)]
+LL + #[inline(always)]
+   |
+LL - #[inline(please,no)]
+LL + #[inline(never)]
    |
 LL - #[inline(please,no)]
 LL + #[inline]
@@ -23,10 +27,13 @@ LL | #[inline()]
    |         |
    |         expected a single argument here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
-LL | #[inline(always|never)]
-   |          ++++++++++++
+LL | #[inline(always)]
+   |          ++++++
+LL | #[inline(never)]
+   |          +++++
 LL - #[inline()]
 LL + #[inline]
    |
diff --git a/tests/ui/issues/issue-43988.stderr b/tests/ui/issues/issue-43988.stderr
index fe61e136a51..b50d691e685 100644
--- a/tests/ui/issues/issue-43988.stderr
+++ b/tests/ui/issues/issue-43988.stderr
@@ -6,10 +6,14 @@ LL |     #[inline(XYZ)]
    |              |
    |              valid arguments are `always` or `never`
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL -     #[inline(XYZ)]
-LL +     #[inline(always|never)]
+LL +     #[inline(always)]
+   |
+LL -     #[inline(XYZ)]
+LL +     #[inline(never)]
    |
 LL -     #[inline(XYZ)]
 LL +     #[inline]
@@ -22,6 +26,7 @@ LL |     #[repr(nothing)]
    |            ^^^^^^^
    |
    = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
 
 error[E0552]: unrecognized representation hint
   --> $DIR/issue-43988.rs:18:12
@@ -30,15 +35,26 @@ LL |     #[repr(something_not_real)]
    |            ^^^^^^^^^^^^^^^^^^
    |
    = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
 
 error[E0539]: malformed `repr` attribute input
   --> $DIR/issue-43988.rs:24:5
    |
 LL |     #[repr]
-   |     ^^^^^^^
-   |     |
-   |     expected this to be a list
-   |     help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
+   |     ^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL |     #[repr(<integer type>)]
+   |           ++++++++++++++++
+LL |     #[repr(C)]
+   |           +++
+LL |     #[repr(Rust)]
+   |           ++++++
+LL |     #[repr(align(...))]
+   |           ++++++++++++
+   = and 2 other candidates
 
 error[E0539]: malformed `inline` attribute input
   --> $DIR/issue-43988.rs:30:5
@@ -48,10 +64,14 @@ LL |     #[inline(ABC)]
    |              |
    |              valid arguments are `always` or `never`
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL -     #[inline(ABC)]
-LL +     #[inline(always|never)]
+LL +     #[inline(always)]
+   |
+LL -     #[inline(ABC)]
+LL +     #[inline(never)]
    |
 LL -     #[inline(ABC)]
 LL +     #[inline]
@@ -61,10 +81,20 @@ error[E0539]: malformed `repr` attribute input
   --> $DIR/issue-43988.rs:34:14
    |
 LL |     let _z = #[repr] 1;
-   |              ^^^^^^^
-   |              |
-   |              expected this to be a list
-   |              help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
+   |              ^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL |     let _z = #[repr(<integer type>)] 1;
+   |                    ++++++++++++++++
+LL |     let _z = #[repr(C)] 1;
+   |                    +++
+LL |     let _z = #[repr(Rust)] 1;
+   |                    ++++++
+LL |     let _z = #[repr(align(...))] 1;
+   |                    ++++++++++++
+   = and 2 other candidates
 
 error[E0518]: attribute should be applied to function or closure
   --> $DIR/issue-43988.rs:5:5
diff --git a/tests/ui/link-native-libs/link-attr-validation-early.rs b/tests/ui/link-native-libs/link-attr-validation-early.rs
index b9a835fb5e9..a7dd80f8920 100644
--- a/tests/ui/link-native-libs/link-attr-validation-early.rs
+++ b/tests/ui/link-native-libs/link-attr-validation-early.rs
@@ -1,7 +1,7 @@
 // Top-level ill-formed
-#[link] //~ ERROR attribute must be of the form
+#[link] //~ ERROR valid forms for the attribute are
         //~| WARN this was previously accepted
-#[link = "foo"] //~ ERROR attribute must be of the form
+#[link = "foo"] //~ ERROR valid forms for the attribute are
                 //~| WARN this was previously accepted
 extern "C" {}
 
diff --git a/tests/ui/link-native-libs/link-attr-validation-early.stderr b/tests/ui/link-native-libs/link-attr-validation-early.stderr
index c69899275d5..36cca6f27ef 100644
--- a/tests/ui/link-native-libs/link-attr-validation-early.stderr
+++ b/tests/ui/link-native-libs/link-attr-validation-early.stderr
@@ -1,4 +1,4 @@
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/link-attr-validation-early.rs:2:1
    |
 LL | #[link]
@@ -6,9 +6,10 @@ LL | #[link]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/link-attr-validation-early.rs:4:1
    |
 LL | #[link = "foo"]
@@ -16,11 +17,12 @@ LL | #[link = "foo"]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
 
 error: aborting due to 2 previous errors
 
 Future incompatibility report: Future breakage diagnostic:
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/link-attr-validation-early.rs:2:1
    |
 LL | #[link]
@@ -28,10 +30,11 @@ LL | #[link]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 Future breakage diagnostic:
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/link-attr-validation-early.rs:4:1
    |
 LL | #[link = "foo"]
@@ -39,5 +42,6 @@ LL | #[link = "foo"]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr
index ffae30aabcc..6bf1eab311a 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-invalid-format.stderr
@@ -6,6 +6,8 @@ LL |     #[link_ordinal("JustMonika")]
    |     |              |
    |     |              expected an integer literal here
    |     help: must be of the form: `#[link_ordinal(ordinal)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute>
 
 error[E0539]: malformed `link_ordinal` attribute input
   --> $DIR/link-ordinal-invalid-format.rs:6:5
@@ -15,6 +17,8 @@ LL |     #[link_ordinal("JustMonika")]
    |     |              |
    |     |              expected an integer literal here
    |     help: must be of the form: `#[link_ordinal(ordinal)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs
index 2a8b9ebacf7..58f19085540 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.rs
@@ -3,10 +3,12 @@ extern "C" {
     #[link_ordinal()]
     //~^ ERROR malformed `link_ordinal` attribute input
     //~| NOTE  expected a single argument
+    //~| NOTE for more information, visit
     fn foo();
     #[link_ordinal()]
     //~^ ERROR malformed `link_ordinal` attribute input
     //~| NOTE  expected a single argument
+    //~| NOTE for more information, visit
     static mut imported_variable: i32;
 }
 
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr
index c6b8a18d03a..d575b0961af 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-missing-argument.stderr
@@ -6,15 +6,19 @@ LL |     #[link_ordinal()]
    |     |             |
    |     |             expected a single argument here
    |     help: must be of the form: `#[link_ordinal(ordinal)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute>
 
 error[E0805]: malformed `link_ordinal` attribute input
-  --> $DIR/link-ordinal-missing-argument.rs:7:5
+  --> $DIR/link-ordinal-missing-argument.rs:8:5
    |
 LL |     #[link_ordinal()]
    |     ^^^^^^^^^^^^^^--^
    |     |             |
    |     |             expected a single argument here
    |     help: must be of the form: `#[link_ordinal(ordinal)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs
index ddf9583352f..55b18ae0d96 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.rs
@@ -3,10 +3,12 @@ extern "C" {
     #[link_ordinal(3, 4)]
     //~^ ERROR malformed `link_ordinal` attribute input
     //~| NOTE  expected a single argument
+    //~| NOTE for more information, visit
     fn foo();
     #[link_ordinal(3, 4)]
     //~^ ERROR malformed `link_ordinal` attribute input
     //~| NOTE  expected a single argument
+    //~| NOTE for more information, visit
     static mut imported_variable: i32;
 }
 
diff --git a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr
index 7d63304f598..a84fef9f9e4 100644
--- a/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr
+++ b/tests/ui/linkage-attr/raw-dylib/windows/link-ordinal-too-many-arguments.stderr
@@ -6,15 +6,19 @@ LL |     #[link_ordinal(3, 4)]
    |     |             |
    |     |             expected a single argument here
    |     help: must be of the form: `#[link_ordinal(ordinal)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute>
 
 error[E0805]: malformed `link_ordinal` attribute input
-  --> $DIR/link-ordinal-too-many-arguments.rs:7:5
+  --> $DIR/link-ordinal-too-many-arguments.rs:8:5
    |
 LL |     #[link_ordinal(3, 4)]
    |     ^^^^^^^^^^^^^^------^
    |     |             |
    |     |             expected a single argument here
    |     help: must be of the form: `#[link_ordinal(ordinal)]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/lint/dead-code/self-assign.rs b/tests/ui/lint/dead-code/self-assign.rs
index 357846baf22..ce91f53cbf1 100644
--- a/tests/ui/lint/dead-code/self-assign.rs
+++ b/tests/ui/lint/dead-code/self-assign.rs
@@ -7,23 +7,20 @@
 //! - `dead_code` lint expansion for self-assignments was implemented in #87129.
 //! - Unfortunately implementation components of #87129 had to be disabled as part of reverts
 //!   #86212, #83171 (to revert #81473) to address regressions #81626 and #81658.
-//! - Consequently, none of the following warnings are emitted.
+//! - Re-enabled in current version to properly detect self-assignments.
 
 //@ check-pass
 
-// Implementation of self-assignment `dead_code` lint expansions disabled due to reverts.
-//@ known-bug: #75356
-
 #![allow(unused_assignments)]
 #![warn(dead_code)]
 
 fn main() {
     let mut x = 0;
     x = x;
-    // FIXME ~^ WARNING: useless assignment of variable of type `i32` to itself
+    //~^ WARNING: useless assignment of variable of type `i32` to itself
 
     x = (x);
-    // FIXME ~^ WARNING: useless assignment of variable of type `i32` to itself
+    //~^ WARNING: useless assignment of variable of type `i32` to itself
 
     x = {x};
     // block expressions don't count as self-assignments
@@ -32,10 +29,10 @@ fn main() {
     struct S<'a> { f: &'a str }
     let mut s = S { f: "abc" };
     s = s;
-    // FIXME ~^ WARNING: useless assignment of variable of type `S` to itself
+    //~^ WARNING: useless assignment of variable of type `S<'_>` to itself
 
     s.f = s.f;
-    // FIXME ~^ WARNING: useless assignment of field of type `&str` to itself
+    //~^ WARNING: useless assignment of field of type `&str` to itself
 
 
     struct N0 { x: Box<i32> }
@@ -44,11 +41,11 @@ fn main() {
     struct N3 { n: N2 };
     let mut n3 = N3 { n: N2(N1 { n: N0 { x: Box::new(42) } }) };
     n3.n.0.n.x = n3.n.0.n.x;
-    // FIXME ~^ WARNING: useless assignment of field of type `Box<i32>` to itself
+    //~^ WARNING: useless assignment of field of type `Box<i32>` to itself
 
     let mut t = (1, ((2, 3, (4, 5)),));
     t.1.0.2.1 = t.1.0.2.1;
-    // FIXME ~^ WARNING: useless assignment of field of type `i32` to itself
+    //~^ WARNING: useless assignment of field of type `i32` to itself
 
 
     let mut y = 0;
diff --git a/tests/ui/lint/dead-code/self-assign.stderr b/tests/ui/lint/dead-code/self-assign.stderr
new file mode 100644
index 00000000000..408ebdb658b
--- /dev/null
+++ b/tests/ui/lint/dead-code/self-assign.stderr
@@ -0,0 +1,44 @@
+warning: useless assignment of variable of type `i32` to itself
+  --> $DIR/self-assign.rs:19:5
+   |
+LL |     x = x;
+   |     ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/self-assign.rs:15:9
+   |
+LL | #![warn(dead_code)]
+   |         ^^^^^^^^^
+
+warning: useless assignment of variable of type `i32` to itself
+  --> $DIR/self-assign.rs:22:5
+   |
+LL |     x = (x);
+   |     ^^^^^^^
+
+warning: useless assignment of variable of type `S<'_>` to itself
+  --> $DIR/self-assign.rs:31:5
+   |
+LL |     s = s;
+   |     ^^^^^
+
+warning: useless assignment of field of type `&str` to itself
+  --> $DIR/self-assign.rs:34:5
+   |
+LL |     s.f = s.f;
+   |     ^^^^^^^^^
+
+warning: useless assignment of field of type `Box<i32>` to itself
+  --> $DIR/self-assign.rs:43:5
+   |
+LL |     n3.n.0.n.x = n3.n.0.n.x;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: useless assignment of field of type `i32` to itself
+  --> $DIR/self-assign.rs:47:5
+   |
+LL |     t.1.0.2.1 = t.1.0.2.1;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
+warning: 6 warnings emitted
+
diff --git a/tests/ui/lint/lint-malformed.stderr b/tests/ui/lint/lint-malformed.stderr
index 0bdcc293b65..25a4298bd75 100644
--- a/tests/ui/lint/lint-malformed.stderr
+++ b/tests/ui/lint/lint-malformed.stderr
@@ -16,7 +16,20 @@ error: malformed `deny` attribute input
   --> $DIR/lint-malformed.rs:1:1
    |
 LL | #![deny = "foo"]
-   | ^^^^^^^^^^^^^^^^ help: must be of the form: `#![deny(lint1, lint2, ..., /*opt*/ reason = "...")]`
+   | ^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes>
+help: the following are the possible correct uses
+   |
+LL - #![deny = "foo"]
+LL + #![deny(lint1)]
+   |
+LL - #![deny = "foo"]
+LL + #![deny(lint1, lint2, ...)]
+   |
+LL - #![deny = "foo"]
+LL + #![deny(lint1, lint2, lint3, reason = "...")]
+   |
 
 error[E0452]: malformed lint attribute input
   --> $DIR/lint-malformed.rs:2:10
diff --git a/tests/ui/macros/macro-use-bad-args-1.stderr b/tests/ui/macros/macro-use-bad-args-1.stderr
index 2f43d0997df..542b4ae2b7a 100644
--- a/tests/ui/macros/macro-use-bad-args-1.stderr
+++ b/tests/ui/macros/macro-use-bad-args-1.stderr
@@ -6,6 +6,7 @@ LL | #[macro_use(foo(bar))]
    |                |
    |                didn't expect any arguments here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[macro_use(foo(bar))]
diff --git a/tests/ui/macros/macro-use-bad-args-2.stderr b/tests/ui/macros/macro-use-bad-args-2.stderr
index d7b03c93588..2db9ffe50b0 100644
--- a/tests/ui/macros/macro-use-bad-args-2.stderr
+++ b/tests/ui/macros/macro-use-bad-args-2.stderr
@@ -6,6 +6,7 @@ LL | #[macro_use(foo="bar")]
    |                |
    |                didn't expect any arguments here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[macro_use(foo="bar")]
diff --git a/tests/ui/malformed/malformed-regressions.rs b/tests/ui/malformed/malformed-regressions.rs
index f0a7aac59c1..99f0fc904a9 100644
--- a/tests/ui/malformed/malformed-regressions.rs
+++ b/tests/ui/malformed/malformed-regressions.rs
@@ -4,9 +4,9 @@
 //~^ WARN this was previously accepted
 #[inline = ""] //~ ERROR valid forms for the attribute are
 //~^ WARN this was previously accepted
-#[link] //~ ERROR attribute must be of the form
+#[link] //~ ERROR valid forms for the attribute are
 //~^ WARN this was previously accepted
-#[link = ""] //~ ERROR attribute must be of the form
+#[link = ""] //~ ERROR valid forms for the attribute are
 //~^ WARN this was previously accepted
 
 fn main() {}
diff --git a/tests/ui/malformed/malformed-regressions.stderr b/tests/ui/malformed/malformed-regressions.stderr
index 60ea5da761d..4a00c9b4a7d 100644
--- a/tests/ui/malformed/malformed-regressions.stderr
+++ b/tests/ui/malformed/malformed-regressions.stderr
@@ -1,4 +1,4 @@
-error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]`
+error: valid forms for the attribute are `#[doc(hidden)]`, `#[doc(inline)]`, and `#[doc = "string"]`
   --> $DIR/malformed-regressions.rs:1:1
    |
 LL | #[doc]
@@ -6,9 +6,10 @@ LL | #[doc]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/malformed-regressions.rs:7:1
    |
 LL | #[link]
@@ -16,8 +17,9 @@ LL | #[link]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
 
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/malformed-regressions.rs:9:1
    |
 LL | #[link = ""]
@@ -25,6 +27,7 @@ LL | #[link = ""]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
 
 error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]`
   --> $DIR/malformed-regressions.rs:3:1
@@ -35,7 +38,7 @@ LL | #[ignore()]
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
 
-error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
   --> $DIR/malformed-regressions.rs:5:1
    |
 LL | #[inline = ""]
@@ -47,7 +50,7 @@ LL | #[inline = ""]
 error: aborting due to 5 previous errors
 
 Future incompatibility report: Future breakage diagnostic:
-error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]`
+error: valid forms for the attribute are `#[doc(hidden)]`, `#[doc(inline)]`, and `#[doc = "string"]`
   --> $DIR/malformed-regressions.rs:1:1
    |
 LL | #[doc]
@@ -55,10 +58,11 @@ LL | #[doc]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 Future breakage diagnostic:
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/malformed-regressions.rs:7:1
    |
 LL | #[link]
@@ -66,10 +70,11 @@ LL | #[link]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 Future breakage diagnostic:
-error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
+error: valid forms for the attribute are `#[link(name = "...")]`, `#[link(name = "...", kind = "dylib|static|...")]`, `#[link(name = "...", wasm_import_module = "...")]`, `#[link(name = "...", import_name_type = "decorated|noprefix|undecorated")]`, and `#[link(name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated")]`
   --> $DIR/malformed-regressions.rs:9:1
    |
 LL | #[link = ""]
@@ -77,6 +82,7 @@ LL | #[link = ""]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute>
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 Future breakage diagnostic:
@@ -91,7 +97,7 @@ LL | #[ignore()]
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 Future breakage diagnostic:
-error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
+error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]`
   --> $DIR/malformed-regressions.rs:5:1
    |
 LL | #[inline = ""]
diff --git a/tests/ui/modules/path-invalid-form.stderr b/tests/ui/modules/path-invalid-form.stderr
index e8ded1343f7..4e9a62fa7a9 100644
--- a/tests/ui/modules/path-invalid-form.stderr
+++ b/tests/ui/modules/path-invalid-form.stderr
@@ -3,6 +3,8 @@ error: malformed `path` attribute input
    |
 LL | #[path = 123]
    | ^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/modules/path-macro.stderr b/tests/ui/modules/path-macro.stderr
index eb02c721edd..fd93871f3a6 100644
--- a/tests/ui/modules/path-macro.stderr
+++ b/tests/ui/modules/path-macro.stderr
@@ -3,6 +3,8 @@ error: malformed `path` attribute input
    |
 LL | #[path = foo!()]
    | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/bad-lit-suffixes.stderr b/tests/ui/parser/bad-lit-suffixes.stderr
index 7876d75c5a4..9a51cf70960 100644
--- a/tests/ui/parser/bad-lit-suffixes.stderr
+++ b/tests/ui/parser/bad-lit-suffixes.stderr
@@ -158,6 +158,7 @@ LL | #[must_use = "string"suffix]
    |              |
    |              expected a string literal here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[must_use = "string"suffix]
diff --git a/tests/ui/parser/default-on-wrong-item-kind.rs b/tests/ui/parser/default-on-wrong-item-kind.rs
index da990a4b421..9de85640565 100644
--- a/tests/ui/parser/default-on-wrong-item-kind.rs
+++ b/tests/ui/parser/default-on-wrong-item-kind.rs
@@ -19,7 +19,7 @@ mod free_items {
     default union foo {} //~ ERROR a union cannot be `default`
     default trait foo {} //~ ERROR a trait cannot be `default`
     default trait foo = Ord; //~ ERROR a trait alias cannot be `default`
-    default impl foo {}
+    default impl foo {} //~ ERROR inherent impls cannot be default
     default!();
     default::foo::bar!();
     default default!(); //~ ERROR an item macro invocation cannot be `default`
@@ -53,7 +53,7 @@ extern "C" {
     //~^ ERROR trait is not supported in `extern` blocks
     default trait foo = Ord; //~ ERROR a trait alias cannot be `default`
     //~^ ERROR trait alias is not supported in `extern` blocks
-    default impl foo {}
+    default impl foo {} //~ ERROR inherent impls cannot be default
     //~^ ERROR implementation is not supported in `extern` blocks
     default!();
     default::foo::bar!();
@@ -90,7 +90,7 @@ impl S {
     //~^ ERROR trait is not supported in `trait`s or `impl`s
     default trait foo = Ord; //~ ERROR a trait alias cannot be `default`
     //~^ ERROR trait alias is not supported in `trait`s or `impl`s
-    default impl foo {}
+    default impl foo {} //~ ERROR inherent impls cannot be default
     //~^ ERROR implementation is not supported in `trait`s or `impl`s
     default!();
     default::foo::bar!();
@@ -127,7 +127,7 @@ trait T {
     //~^ ERROR trait is not supported in `trait`s or `impl`s
     default trait foo = Ord; //~ ERROR a trait alias cannot be `default`
     //~^ ERROR trait alias is not supported in `trait`s or `impl`s
-    default impl foo {}
+    default impl foo {} //~ ERROR inherent impls cannot be default
     //~^ ERROR implementation is not supported in `trait`s or `impl`s
     default!();
     default::foo::bar!();
diff --git a/tests/ui/parser/default-on-wrong-item-kind.stderr b/tests/ui/parser/default-on-wrong-item-kind.stderr
index 56641565b16..0380fcdf775 100644
--- a/tests/ui/parser/default-on-wrong-item-kind.stderr
+++ b/tests/ui/parser/default-on-wrong-item-kind.stderr
@@ -78,6 +78,16 @@ LL |     default trait foo = Ord;
    |
    = note: only associated `fn`, `const`, and `type` items can be `default`
 
+error: inherent impls cannot be default
+  --> $DIR/default-on-wrong-item-kind.rs:22:18
+   |
+LL |     default impl foo {}
+   |     -------      ^^^ inherent impl for this type
+   |     |
+   |     default because of this
+   |
+   = note: only trait implementations may be annotated with `default`
+
 error: an item macro invocation cannot be `default`
   --> $DIR/default-on-wrong-item-kind.rs:25:5
    |
@@ -275,6 +285,16 @@ LL |     default trait foo = Ord;
    |
    = help: consider moving the trait alias out to a nearby module scope
 
+error: inherent impls cannot be default
+  --> $DIR/default-on-wrong-item-kind.rs:56:18
+   |
+LL |     default impl foo {}
+   |     -------      ^^^ inherent impl for this type
+   |     |
+   |     default because of this
+   |
+   = note: only trait implementations may be annotated with `default`
+
 error: implementation is not supported in `extern` blocks
   --> $DIR/default-on-wrong-item-kind.rs:56:5
    |
@@ -489,6 +509,16 @@ LL |     default trait foo = Ord;
    |
    = help: consider moving the trait alias out to a nearby module scope
 
+error: inherent impls cannot be default
+  --> $DIR/default-on-wrong-item-kind.rs:93:18
+   |
+LL |     default impl foo {}
+   |     -------      ^^^ inherent impl for this type
+   |     |
+   |     default because of this
+   |
+   = note: only trait implementations may be annotated with `default`
+
 error: implementation is not supported in `trait`s or `impl`s
   --> $DIR/default-on-wrong-item-kind.rs:93:5
    |
@@ -703,6 +733,16 @@ LL |     default trait foo = Ord;
    |
    = help: consider moving the trait alias out to a nearby module scope
 
+error: inherent impls cannot be default
+  --> $DIR/default-on-wrong-item-kind.rs:130:18
+   |
+LL |     default impl foo {}
+   |     -------      ^^^ inherent impl for this type
+   |     |
+   |     default because of this
+   |
+   = note: only trait implementations may be annotated with `default`
+
 error: implementation is not supported in `trait`s or `impl`s
   --> $DIR/default-on-wrong-item-kind.rs:130:5
    |
@@ -759,5 +799,5 @@ LL |     default macro_rules! foo {}
    |
    = help: consider moving the macro definition out to a nearby module scope
 
-error: aborting due to 95 previous errors
+error: aborting due to 99 previous errors
 
diff --git a/tests/ui/proc-macro/attribute.rs b/tests/ui/proc-macro/attribute.rs
index 988cdcd0403..dfb26738377 100644
--- a/tests/ui/proc-macro/attribute.rs
+++ b/tests/ui/proc-macro/attribute.rs
@@ -9,46 +9,55 @@ use proc_macro::*;
 #[proc_macro_derive]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE expected this to be a list
+//~| NOTE for more information, visit
 pub fn foo1(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive = ""]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE expected this to be a list
+//~| NOTE for more information, visit
 pub fn foo2(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d3, a, b)]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE the only valid argument here is `attributes`
+//~| NOTE for more information, visit
 pub fn foo3(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d4, attributes(a), b)]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE didn't expect any arguments here
+//~| NOTE for more information, visit
 pub fn foo4(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive("a")]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE didn't expect a literal here
+//~| NOTE for more information, visit
 pub fn foo5(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d6 = "")]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE didn't expect any arguments here
+//~| NOTE for more information, visit
 pub fn foo6(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(m::d7)]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE expected a valid identifier here
+//~| NOTE for more information, visit
 pub fn foo7(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d8(a))]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE didn't expect any arguments here
+//~| NOTE for more information, visit
 pub fn foo8(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(self)]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE expected a valid identifier here
+//~| NOTE for more information, visit
 pub fn foo9(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(PartialEq)] // OK
@@ -57,34 +66,41 @@ pub fn foo10(input: TokenStream) -> TokenStream { input }
 #[proc_macro_derive(d11, a)]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE the only valid argument here is `attributes`
+//~| NOTE for more information, visit
 pub fn foo11(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d12, attributes)]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE expected this to be a list
+//~| NOTE for more information, visit
 pub fn foo12(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d13, attributes("a"))]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE expected a valid identifier here
+//~| NOTE for more information, visit
 pub fn foo13(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d14, attributes(a = ""))]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE didn't expect any arguments here
+//~| NOTE for more information, visit
 pub fn foo14(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d15, attributes(m::a))]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE expected a valid identifier here
+//~| NOTE for more information, visit
 pub fn foo15(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d16, attributes(a(b)))]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE didn't expect any arguments here
+//~| NOTE for more information, visit
 pub fn foo16(input: TokenStream) -> TokenStream { input }
 
 #[proc_macro_derive(d17, attributes(self))]
 //~^ ERROR malformed `proc_macro_derive` attribute
 //~| NOTE expected a valid identifier here
+//~| NOTE for more information, visit
 pub fn foo17(input: TokenStream) -> TokenStream { input }
diff --git a/tests/ui/proc-macro/attribute.stderr b/tests/ui/proc-macro/attribute.stderr
index db59a1fdfb3..e7127c8ef1d 100644
--- a/tests/ui/proc-macro/attribute.stderr
+++ b/tests/ui/proc-macro/attribute.stderr
@@ -2,145 +2,283 @@ error[E0539]: malformed `proc_macro_derive` attribute input
   --> $DIR/attribute.rs:9:1
    |
 LL | #[proc_macro_derive]
-   | ^^^^^^^^^^^^^^^^^^^^
-   | |
-   | expected this to be a list
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   | ^^^^^^^^^^^^^^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL | #[proc_macro_derive(TraitName)]
+   |                    +++++++++++
+LL | #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |                    ++++++++++++++++++++++++++++++++++++++++++
 
 error[E0539]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:14:1
+  --> $DIR/attribute.rs:15:1
    |
 LL | #[proc_macro_derive = ""]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   | |
-   | expected this to be a list
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive = ""]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive = ""]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0539]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:19:1
+  --> $DIR/attribute.rs:21:1
    |
 LL | #[proc_macro_derive(d3, a, b)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^-^^^^^
-   | |                       |
-   | |                       the only valid argument here is `attributes`
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                         |
+   |                         the only valid argument here is `attributes`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d3, a, b)]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d3, a, b)]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0565]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:24:1
+  --> $DIR/attribute.rs:27:1
    |
 LL | #[proc_macro_derive(d4, attributes(a), b)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^
-   | |                                      |
-   | |                                      didn't expect any arguments here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                                        |
+   |                                        didn't expect any arguments here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d4, attributes(a), b)]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d4, attributes(a), b)]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0565]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:29:1
+  --> $DIR/attribute.rs:33:1
    |
 LL | #[proc_macro_derive("a")]
    | ^^^^^^^^^^^^^^^^^^^^---^^
-   | |                   |
-   | |                   didn't expect a literal here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                     |
+   |                     didn't expect a literal here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive("a")]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive("a")]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0565]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:34:1
+  --> $DIR/attribute.rs:39:1
    |
 LL | #[proc_macro_derive(d6 = "")]
    | ^^^^^^^^^^^^^^^^^^^^^^^----^^
-   | |                      |
-   | |                      didn't expect any arguments here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                        |
+   |                        didn't expect any arguments here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d6 = "")]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d6 = "")]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0539]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:39:1
+  --> $DIR/attribute.rs:45:1
    |
 LL | #[proc_macro_derive(m::d7)]
    | ^^^^^^^^^^^^^^^^^^^^-----^^
-   | |                   |
-   | |                   expected a valid identifier here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                     |
+   |                     expected a valid identifier here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(m::d7)]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(m::d7)]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0565]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:44:1
+  --> $DIR/attribute.rs:51:1
    |
 LL | #[proc_macro_derive(d8(a))]
    | ^^^^^^^^^^^^^^^^^^^^^^---^^
-   | |                     |
-   | |                     didn't expect any arguments here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                       |
+   |                       didn't expect any arguments here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d8(a))]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d8(a))]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0539]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:49:1
+  --> $DIR/attribute.rs:57:1
    |
 LL | #[proc_macro_derive(self)]
    | ^^^^^^^^^^^^^^^^^^^^----^^
-   | |                   |
-   | |                   expected a valid identifier here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                     |
+   |                     expected a valid identifier here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(self)]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(self)]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0539]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:57:1
+  --> $DIR/attribute.rs:66:1
    |
 LL | #[proc_macro_derive(d11, a)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^-^^
-   | |                        |
-   | |                        the only valid argument here is `attributes`
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                          |
+   |                          the only valid argument here is `attributes`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d11, a)]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d11, a)]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0539]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:62:1
+  --> $DIR/attribute.rs:72:1
    |
 LL | #[proc_macro_derive(d12, attributes)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^----------^^
-   | |                        |
-   | |                        expected this to be a list
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                          |
+   |                          expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d12, attributes)]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d12, attributes)]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0539]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:67:1
+  --> $DIR/attribute.rs:78:1
    |
 LL | #[proc_macro_derive(d13, attributes("a"))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^
-   | |                                   |
-   | |                                   expected a valid identifier here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                                     |
+   |                                     expected a valid identifier here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d13, attributes("a"))]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d13, attributes("a"))]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0565]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:72:1
+  --> $DIR/attribute.rs:84:1
    |
 LL | #[proc_macro_derive(d14, attributes(a = ""))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----^^^
-   | |                                     |
-   | |                                     didn't expect any arguments here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                                       |
+   |                                       didn't expect any arguments here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d14, attributes(a = ""))]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d14, attributes(a = ""))]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0539]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:77:1
+  --> $DIR/attribute.rs:90:1
    |
 LL | #[proc_macro_derive(d15, attributes(m::a))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----^^^
-   | |                                   |
-   | |                                   expected a valid identifier here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                                     |
+   |                                     expected a valid identifier here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d15, attributes(m::a))]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d15, attributes(m::a))]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0565]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:82:1
+  --> $DIR/attribute.rs:96:1
    |
 LL | #[proc_macro_derive(d16, attributes(a(b)))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^
-   | |                                    |
-   | |                                    didn't expect any arguments here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                                      |
+   |                                      didn't expect any arguments here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d16, attributes(a(b)))]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d16, attributes(a(b)))]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error[E0539]: malformed `proc_macro_derive` attribute input
-  --> $DIR/attribute.rs:87:1
+  --> $DIR/attribute.rs:102:1
    |
 LL | #[proc_macro_derive(d17, attributes(self))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----^^^
-   | |                                   |
-   | |                                   expected a valid identifier here
-   | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
+   |                                     |
+   |                                     expected a valid identifier here
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[proc_macro_derive(d17, attributes(self))]
+LL + #[proc_macro_derive(TraitName)]
+   |
+LL - #[proc_macro_derive(d17, attributes(self))]
+LL + #[proc_macro_derive(TraitName, attributes(name1, name2, ...))]
+   |
 
 error: aborting due to 16 previous errors
 
diff --git a/tests/ui/recursion_limit/invalid_digit_type.stderr b/tests/ui/recursion_limit/invalid_digit_type.stderr
index 94442b5747f..a122262f1df 100644
--- a/tests/ui/recursion_limit/invalid_digit_type.stderr
+++ b/tests/ui/recursion_limit/invalid_digit_type.stderr
@@ -3,6 +3,8 @@ error: malformed `recursion_limit` attribute input
    |
 LL | #![recursion_limit = 123]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![recursion_limit = "N"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/recursion_limit/invalid_macro.stderr b/tests/ui/recursion_limit/invalid_macro.stderr
index aa4894ec4d8..b4dbc9fcb13 100644
--- a/tests/ui/recursion_limit/invalid_macro.stderr
+++ b/tests/ui/recursion_limit/invalid_macro.stderr
@@ -3,6 +3,8 @@ error: malformed `recursion_limit` attribute input
    |
 LL | #![recursion_limit = foo!()]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![recursion_limit = "N"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/recursion_limit/no-value.stderr b/tests/ui/recursion_limit/no-value.stderr
index b2e8c46c372..4a0ad04f271 100644
--- a/tests/ui/recursion_limit/no-value.stderr
+++ b/tests/ui/recursion_limit/no-value.stderr
@@ -3,6 +3,8 @@ error: malformed `recursion_limit` attribute input
    |
 LL | #![recursion_limit]
    | ^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![recursion_limit = "N"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/repr/invalid_repr_list_help.stderr b/tests/ui/repr/invalid_repr_list_help.stderr
index 763ad9c2795..f9d1593275e 100644
--- a/tests/ui/repr/invalid_repr_list_help.stderr
+++ b/tests/ui/repr/invalid_repr_list_help.stderr
@@ -5,6 +5,7 @@ LL | #[repr(uwu)]
    |        ^^^
    |
    = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
 
 error[E0552]: unrecognized representation hint
   --> $DIR/invalid_repr_list_help.rs:6:8
@@ -13,6 +14,7 @@ LL | #[repr(uwu = "a")]
    |        ^^^^^^^^^
    |
    = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
 
 error[E0552]: unrecognized representation hint
   --> $DIR/invalid_repr_list_help.rs:9:8
@@ -21,6 +23,7 @@ LL | #[repr(uwu(4))]
    |        ^^^^^^
    |
    = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
 
 error[E0552]: unrecognized representation hint
   --> $DIR/invalid_repr_list_help.rs:14:8
@@ -29,6 +32,7 @@ LL | #[repr(uwu, u8)]
    |        ^^^
    |
    = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
 
 error[E0552]: unrecognized representation hint
   --> $DIR/invalid_repr_list_help.rs:19:8
@@ -37,6 +41,7 @@ LL | #[repr(uwu)]
    |        ^^^
    |
    = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
 
 error: unknown `doc` attribute `owo`
   --> $DIR/invalid_repr_list_help.rs:20:7
diff --git a/tests/ui/repr/repr.stderr b/tests/ui/repr/repr.stderr
index 9e581332278..d4faea12517 100644
--- a/tests/ui/repr/repr.stderr
+++ b/tests/ui/repr/repr.stderr
@@ -2,28 +2,66 @@ error[E0539]: malformed `repr` attribute input
   --> $DIR/repr.rs:1:1
    |
 LL | #[repr]
-   | ^^^^^^^
-   | |
-   | expected this to be a list
-   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
+   | ^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL | #[repr(<integer type>)]
+   |       ++++++++++++++++
+LL | #[repr(C)]
+   |       +++
+LL | #[repr(Rust)]
+   |       ++++++
+LL | #[repr(align(...))]
+   |       ++++++++++++
+   = and 2 other candidates
 
 error[E0539]: malformed `repr` attribute input
   --> $DIR/repr.rs:4:1
    |
 LL | #[repr = "B"]
-   | ^^^^^^^^^^^^^
-   | |
-   | expected this to be a list
-   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
+   | ^^^^^^^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[repr = "B"]
+LL + #[repr(<integer type>)]
+   |
+LL - #[repr = "B"]
+LL + #[repr(C)]
+   |
+LL - #[repr = "B"]
+LL + #[repr(Rust)]
+   |
+LL - #[repr = "B"]
+LL + #[repr(align(...))]
+   |
+   = and 2 other candidates
 
 error[E0539]: malformed `repr` attribute input
   --> $DIR/repr.rs:7:1
    |
 LL | #[repr = "C"]
-   | ^^^^^^^^^^^^^
-   | |
-   | expected this to be a list
-   | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
+   | ^^^^^^^^^^^^^ expected this to be a list
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/type-layout.html#representations>
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[repr = "C"]
+LL + #[repr(<integer type>)]
+   |
+LL - #[repr = "C"]
+LL + #[repr(C)]
+   |
+LL - #[repr = "C"]
+LL + #[repr(Rust)]
+   |
+LL - #[repr = "C"]
+LL + #[repr(align(...))]
+   |
+   = and 2 other candidates
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/resolve/path-attr-in-const-block.stderr b/tests/ui/resolve/path-attr-in-const-block.stderr
index 0b5942a287d..f3ae5b60c4f 100644
--- a/tests/ui/resolve/path-attr-in-const-block.stderr
+++ b/tests/ui/resolve/path-attr-in-const-block.stderr
@@ -12,6 +12,8 @@ LL |         #![path = foo!()]
    |         |         |
    |         |         expected a string literal here
    |         help: must be of the form: `#[path = "file"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute>
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/span/E0539.stderr b/tests/ui/span/E0539.stderr
index 01f091a2676..34d81b73cb5 100644
--- a/tests/ui/span/E0539.stderr
+++ b/tests/ui/span/E0539.stderr
@@ -6,10 +6,14 @@ LL | #[inline(unknown)]
    |          |
    |          valid arguments are `always` or `never`
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[inline(unknown)]
-LL + #[inline(always|never)]
+LL + #[inline(always)]
+   |
+LL - #[inline(unknown)]
+LL + #[inline(never)]
    |
 LL - #[inline(unknown)]
 LL + #[inline]
diff --git a/tests/ui/specialization/defaultimpl/validation.rs b/tests/ui/specialization/defaultimpl/validation.rs
index 14771be8982..78f6099c3dd 100644
--- a/tests/ui/specialization/defaultimpl/validation.rs
+++ b/tests/ui/specialization/defaultimpl/validation.rs
@@ -4,7 +4,7 @@
 struct S;
 struct Z;
 
-default impl S {} //~ ERROR inherent impls cannot be `default`
+default impl S {} //~ ERROR inherent impls cannot be default
 
 default unsafe impl Send for S {}
 //~^ ERROR impls of auto traits cannot be default
diff --git a/tests/ui/specialization/defaultimpl/validation.stderr b/tests/ui/specialization/defaultimpl/validation.stderr
index 82a33bf9cdc..a8dfef2dcfb 100644
--- a/tests/ui/specialization/defaultimpl/validation.stderr
+++ b/tests/ui/specialization/defaultimpl/validation.stderr
@@ -1,10 +1,10 @@
-error: inherent impls cannot be `default`
+error: inherent impls cannot be default
   --> $DIR/validation.rs:7:14
    |
 LL | default impl S {}
    | -------      ^ inherent impl for this type
    | |
-   | `default` because of this
+   | default because of this
    |
    = note: only trait implementations may be annotated with `default`
 
diff --git a/tests/ui/test-attrs/test-should-panic-attr.rs b/tests/ui/test-attrs/test-should-panic-attr.rs
index af54689551c..e6de07d0094 100644
--- a/tests/ui/test-attrs/test-should-panic-attr.rs
+++ b/tests/ui/test-attrs/test-should-panic-attr.rs
@@ -10,6 +10,7 @@ fn test1() {
 #[should_panic(expected)]
 //~^ ERROR malformed `should_panic` attribute input
 //~| NOTE expected this to be of the form `expected = "..."`
+//~| NOTE for more information, visit
 fn test2() {
     panic!();
 }
@@ -18,6 +19,7 @@ fn test2() {
 #[should_panic(expect)]
 //~^ ERROR malformed `should_panic` attribute input
 //~| NOTE the only valid argument here is "expected"
+//~| NOTE for more information, visit
 fn test3() {
     panic!();
 }
@@ -26,6 +28,7 @@ fn test3() {
 #[should_panic(expected(foo, bar))]
 //~^ ERROR malformed `should_panic` attribute input
 //~| NOTE expected this to be of the form `expected = "..."`
+//~| NOTE for more information, visit
 fn test4() {
     panic!();
 }
@@ -34,6 +37,7 @@ fn test4() {
 #[should_panic(expected = "foo", bar)]
 //~^ ERROR malformed `should_panic` attribute input
 //~| NOTE expected a single argument here
+//~| NOTE for more information, visit
 fn test5() {
     panic!();
 }
diff --git a/tests/ui/test-attrs/test-should-panic-attr.stderr b/tests/ui/test-attrs/test-should-panic-attr.stderr
index 5dfc8e503e8..475a55ad0cb 100644
--- a/tests/ui/test-attrs/test-should-panic-attr.stderr
+++ b/tests/ui/test-attrs/test-should-panic-attr.stderr
@@ -6,6 +6,7 @@ LL | #[should_panic(expected)]
    |                |
    |                expected this to be of the form `expected = "..."`
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[should_panic(expected)]
@@ -18,13 +19,14 @@ LL + #[should_panic]
    |
 
 error[E0539]: malformed `should_panic` attribute input
-  --> $DIR/test-should-panic-attr.rs:18:1
+  --> $DIR/test-should-panic-attr.rs:19:1
    |
 LL | #[should_panic(expect)]
    | ^^^^^^^^^^^^^^--------^
    |               |
    |               the only valid argument here is "expected"
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[should_panic(expect)]
@@ -37,13 +39,14 @@ LL + #[should_panic]
    |
 
 error[E0539]: malformed `should_panic` attribute input
-  --> $DIR/test-should-panic-attr.rs:26:1
+  --> $DIR/test-should-panic-attr.rs:28:1
    |
 LL | #[should_panic(expected(foo, bar))]
    | ^^^^^^^^^^^^^^^------------------^^
    |                |
    |                expected this to be of the form `expected = "..."`
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[should_panic(expected(foo, bar))]
@@ -57,13 +60,14 @@ LL + #[should_panic]
    |
 
 error[E0805]: malformed `should_panic` attribute input
-  --> $DIR/test-should-panic-attr.rs:34:1
+  --> $DIR/test-should-panic-attr.rs:37:1
    |
 LL | #[should_panic(expected = "foo", bar)]
    | ^^^^^^^^^^^^^^-----------------------^
    |               |
    |               expected a single argument here
    |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute>
 help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[should_panic(expected = "foo", bar)]
diff --git a/tests/ui/traits/const-traits/inherent-impl.rs b/tests/ui/traits/const-traits/inherent-impl.rs
index 07b23adf9e1..5ca4c5d7863 100644
--- a/tests/ui/traits/const-traits/inherent-impl.rs
+++ b/tests/ui/traits/const-traits/inherent-impl.rs
@@ -5,9 +5,9 @@ struct S;
 trait T {}
 
 impl const S {}
-//~^ ERROR inherent impls cannot be `const`
+//~^ ERROR inherent impls cannot be const
 
 impl const dyn T {}
-//~^ ERROR inherent impls cannot be `const`
+//~^ ERROR inherent impls cannot be const
 
 fn main() {}
diff --git a/tests/ui/traits/const-traits/inherent-impl.stderr b/tests/ui/traits/const-traits/inherent-impl.stderr
index e4ec1e807b0..d828ecb6349 100644
--- a/tests/ui/traits/const-traits/inherent-impl.stderr
+++ b/tests/ui/traits/const-traits/inherent-impl.stderr
@@ -1,20 +1,20 @@
-error: inherent impls cannot be `const`
+error: inherent impls cannot be const
   --> $DIR/inherent-impl.rs:7:12
    |
 LL | impl const S {}
    |      ----- ^ inherent impl for this type
    |      |
-   |      `const` because of this
+   |      const because of this
    |
    = note: only trait implementations may be annotated with `const`
 
-error: inherent impls cannot be `const`
+error: inherent impls cannot be const
   --> $DIR/inherent-impl.rs:10:12
    |
 LL | impl const dyn T {}
    |      ----- ^^^^^ inherent impl for this type
    |      |
-   |      `const` because of this
+   |      const because of this
    |
    = note: only trait implementations may be annotated with `const`
 
diff --git a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr
index af160a45f3e..9fad260f0be 100644
--- a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr
+++ b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr
@@ -1,15 +1,4 @@
-error: macro expansion ignores keyword `dyn` and any tokens following
-  --> $DIR/macro-const-trait-bound-theoretical-regression.rs:14:31
-   |
-LL |     (dyn $c:ident Trait) => { dyn $c Trait {} };
-   |                               ^^^
-...
-LL | demo! { dyn const Trait }
-   | ------------------------- caused by the macro expansion here
-   |
-   = note: the usage of `demo!` is likely invalid in item context
-
-error: inherent impls cannot be `const`
+error: inherent impls cannot be const
   --> $DIR/macro-const-trait-bound-theoretical-regression.rs:10:40
    |
 LL |     (impl $c:ident Trait) => { impl $c Trait {} };
@@ -18,12 +7,23 @@ LL |     (impl $c:ident Trait) => { impl $c Trait {} };
 LL | demo! { impl const Trait }
    | --------------------------
    | |            |
-   | |            `const` because of this
+   | |            const because of this
    | in this macro invocation
    |
    = note: only trait implementations may be annotated with `const`
    = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
+error: macro expansion ignores keyword `dyn` and any tokens following
+  --> $DIR/macro-const-trait-bound-theoretical-regression.rs:14:31
+   |
+LL |     (dyn $c:ident Trait) => { dyn $c Trait {} };
+   |                               ^^^
+...
+LL | demo! { dyn const Trait }
+   | ------------------------- caused by the macro expansion here
+   |
+   = note: the usage of `demo!` is likely invalid in item context
+
 error[E0658]: const trait impls are experimental
   --> $DIR/macro-const-trait-bound-theoretical-regression.rs:17:14
    |
diff --git a/tests/ui/traits/const-traits/span-bug-issue-121418.rs b/tests/ui/traits/const-traits/span-bug-issue-121418.rs
index 50a7e12f2a7..593180ac094 100644
--- a/tests/ui/traits/const-traits/span-bug-issue-121418.rs
+++ b/tests/ui/traits/const-traits/span-bug-issue-121418.rs
@@ -4,7 +4,7 @@ struct S;
 trait T {}
 
 impl const dyn T {
-    //~^ ERROR inherent impls cannot be `const`
+    //~^ ERROR inherent impls cannot be const
     pub const fn new() -> std::sync::Mutex<dyn T> {}
     //~^ ERROR mismatched types
     //~| ERROR cannot be known at compilation time
diff --git a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr
index f31129d9cb7..0c8ca918a3e 100644
--- a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr
+++ b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr
@@ -1,10 +1,10 @@
-error: inherent impls cannot be `const`
+error: inherent impls cannot be const
   --> $DIR/span-bug-issue-121418.rs:6:12
    |
 LL | impl const dyn T {
    |      ----- ^^^^^ inherent impl for this type
    |      |
-   |      `const` because of this
+   |      const because of this
    |
    = note: only trait implementations may be annotated with `const`
 
diff --git a/tests/ui/traits/safety-inherent-impl.stderr b/tests/ui/traits/safety-inherent-impl.stderr
index 2513fef909e..45cdbe2b523 100644
--- a/tests/ui/traits/safety-inherent-impl.stderr
+++ b/tests/ui/traits/safety-inherent-impl.stderr
@@ -5,6 +5,8 @@ LL | unsafe impl SomeStruct {
    | ------      ^^^^^^^^^^ inherent impl for this type
    | |
    | unsafe because of this
+   |
+   = note: only trait implementations may be annotated with `unsafe`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/traits/syntax-trait-polarity.stderr b/tests/ui/traits/syntax-trait-polarity.stderr
index 1fd40fb6657..8ffcdc7d8b5 100644
--- a/tests/ui/traits/syntax-trait-polarity.stderr
+++ b/tests/ui/traits/syntax-trait-polarity.stderr
@@ -5,15 +5,8 @@ LL | impl !TestType {}
    |      -^^^^^^^^ inherent impl for this type
    |      |
    |      negative because of this
-
-error[E0198]: negative impls cannot be unsafe
-  --> $DIR/syntax-trait-polarity.rs:12:13
    |
-LL | unsafe impl !Send for TestType {}
-   | ------      -^^^^
-   | |           |
-   | |           negative because of this
-   | unsafe because of this
+   = note: only trait implementations may be annotated with `!`
 
 error: inherent impls cannot be negative
   --> $DIR/syntax-trait-polarity.rs:18:10
@@ -22,6 +15,17 @@ LL | impl<T> !TestType2<T> {}
    |         -^^^^^^^^^^^^ inherent impl for this type
    |         |
    |         negative because of this
+   |
+   = note: only trait implementations may be annotated with `!`
+
+error[E0198]: negative impls cannot be unsafe
+  --> $DIR/syntax-trait-polarity.rs:12:13
+   |
+LL | unsafe impl !Send for TestType {}
+   | ------      -^^^^
+   | |           |
+   | |           negative because of this
+   | unsafe because of this
 
 error[E0198]: negative impls cannot be unsafe
   --> $DIR/syntax-trait-polarity.rs:21:16
diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout
index 9cfa65f5801..e9823c9575b 100644
--- a/tests/ui/unpretty/exhaustive.hir.stdout
+++ b/tests/ui/unpretty/exhaustive.hir.stdout
@@ -509,7 +509,7 @@ mod items {
         impl () { }
         impl <T> () { }
         impl Default for () { }
-        impl const <T> Default for () { }
+        impl <T> const Default for () { }
     }
     /// ItemKind::MacCall
     mod item_mac_call { }
diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.fail.stderr b/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.fail.stderr
new file mode 100644
index 00000000000..69be101a40d
--- /dev/null
+++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.fail.stderr
@@ -0,0 +1,18 @@
+error: unstable feature `foo` is used without being enabled.
+  --> $DIR/unstable_feature_bound_on_trait.rs:28:5
+   |
+LL |     Foo::bar();
+   |     ^^^^^^^^^^
+   |
+   = help: The feature can be enabled by marking the current item with `#[unstable_feature_bound(foo)]`
+note: required by a bound in `Bar::bar`
+  --> $DIR/unstable_feature_bound_on_trait.rs:16:1
+   |
+LL | #[unstable_feature_bound(foo)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Bar::bar`
+...
+LL |     fn bar() {}
+   |        --- required by a bound in this associated function
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.rs b/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.rs
new file mode 100644
index 00000000000..0ee00d5e7fb
--- /dev/null
+++ b/tests/ui/unstable-feature-bound/unstable_feature_bound_on_trait.rs
@@ -0,0 +1,33 @@
+//@ revisions: pass fail
+//@[pass] check-pass
+
+#![allow(internal_features)]
+#![feature(staged_api)]
+#![stable(feature = "a", since = "1.1.1" )]
+
+/// Test the behaviour of marking a trait with #[unstable_feature_bound].
+/// In this testcase, even though the trait method `bar` and the `struct Foo` are
+/// both stable, #[unstable_feature_bound] is still needed at the call site of Foo::bar().
+
+#[stable(feature = "a", since = "1.1.1" )]
+struct Foo;
+
+#[unstable(feature = "foo", issue = "none" )]
+#[unstable_feature_bound(foo)]
+trait Bar {
+    #[stable(feature = "a", since = "1.1.1" )]
+    fn bar() {}
+}
+
+#[unstable_feature_bound(foo)]
+impl Bar for Foo {
+}
+
+#[cfg_attr(pass, unstable_feature_bound(foo))]
+fn moo() {
+    Foo::bar();
+    //[fail]~^ ERROR: unstable feature `foo` is used without being enabled.
+}
+
+
+fn main() {}
diff --git a/tests/ui/unstable-feature-bound/unstable_inherent_method.rs b/tests/ui/unstable-feature-bound/unstable_inherent_method.rs
index 5f3095430a8..0d6e4ebb408 100644
--- a/tests/ui/unstable-feature-bound/unstable_inherent_method.rs
+++ b/tests/ui/unstable-feature-bound/unstable_inherent_method.rs
@@ -9,14 +9,14 @@
 pub trait Trait {
     #[unstable(feature = "feat", issue = "none" )]
     #[unstable_feature_bound(foo)]
-    //~^ ERROR: attribute should be applied to `impl` or free function outside of any `impl` or trait
+    //~^ ERROR: attribute should be applied to `impl`, trait or free function
     fn foo();
 }
 
 #[stable(feature = "a", since = "1.1.1" )]
 impl Trait for u8 {
     #[unstable_feature_bound(foo)]
-    //~^ ERROR: attribute should be applied to `impl` or free function outside of any `impl` or trait
+    //~^ ERROR: attribute should be applied to `impl`, trait or free function
     fn foo() {}
 }
 
diff --git a/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr b/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr
index fa1c39db259..90cbb32df7c 100644
--- a/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr
+++ b/tests/ui/unstable-feature-bound/unstable_inherent_method.stderr
@@ -1,20 +1,20 @@
-error: attribute should be applied to `impl` or free function outside of any `impl` or trait
+error: attribute should be applied to `impl`, trait or free function
   --> $DIR/unstable_inherent_method.rs:11:5
    |
 LL |     #[unstable_feature_bound(foo)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 LL |
 LL |     fn foo();
-   |     --------- not an `impl` or free function
+   |     --------- not an `impl`, trait or free function
 
-error: attribute should be applied to `impl` or free function outside of any `impl` or trait
+error: attribute should be applied to `impl`, trait or free function
   --> $DIR/unstable_inherent_method.rs:18:5
    |
 LL |     #[unstable_feature_bound(foo)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 LL |
 LL |     fn foo() {}
-   |     ----------- not an `impl` or free function
+   |     ----------- not an `impl`, trait or free function
 
 error: aborting due to 2 previous errors
 
diff --git a/triagebot.toml b/triagebot.toml
index 71f1ed0fda3..6f6e95c5b50 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -207,6 +207,7 @@ trigger_labels = [
     "regression-from-stable-to-beta",
     "regression-from-stable-to-nightly",
     "I-unsound",
+    "I-miscompile",
 ]
 exclude_labels = [
     "P-*",