about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-08-12 20:37:53 +1000
committerGitHub <noreply@github.com>2025-08-12 20:37:53 +1000
commitd862ae2fce07c32252adc95bf32904fc74bbc532 (patch)
tree5f64932ad738f1920023a0393a82ed1714ab9725
parent38483d8eb156f5947deedf3ca2654e2ac560a082 (diff)
parent928dd114377f5a0c9db521d85987f0df73ca254d (diff)
downloadrust-d862ae2fce07c32252adc95bf32904fc74bbc532.tar.gz
rust-d862ae2fce07c32252adc95bf32904fc74bbc532.zip
Rollup merge of #145238 - estebank:attr-overhaul, r=jdonszelmann
Tweak invalid builtin attribute output

 - Add link to reference/docs when possible
 - More accurate suggestions by supporting multiple alternative suggestions

```
error: malformed `crate_type` attribute input
  --> $DIR/crate-type-macro-call.rs:1:1
   |
LL | #![crate_type = foo!()]
   | ^^^^^^^^^^^^^^^^^^^^^^^
   |
   = 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
```
-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.rs5
-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/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_feature/src/builtin_attrs.rs457
-rw-r--r--compiler/rustc_lint/messages.ftl1
-rw-r--r--compiler/rustc_lint/src/early/diagnostics.rs4
-rw-r--r--compiler/rustc_lint/src/lints.rs3
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs1
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs41
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-96721.stderr2
-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.stderr195
-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/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/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/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/test-attrs/test-should-panic-attr.rs4
-rw-r--r--tests/ui/test-attrs/test-should-panic-attr.stderr10
84 files changed, 1276 insertions, 391 deletions
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 0779248e1a9..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> = &[(
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/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_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_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/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/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_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/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/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/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 74110183450..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]
@@ -172,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]
@@ -180,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]
@@ -190,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]
@@ -199,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
@@ -251,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(since = "version")]
    |
 LL - #[deprecated = 5]
-LL + #[deprecated]
+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
@@ -294,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
@@ -357,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]
    |
@@ -386,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
@@ -450,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]
@@ -463,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
@@ -521,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
@@ -621,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]
@@ -664,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]
@@ -672,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]
@@ -683,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]
@@ -694,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/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/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/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/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)]