about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs59
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs9
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs17
-rw-r--r--compiler/rustc_error_messages/locales/en-US/infer.ftl4
-rw-r--r--compiler/rustc_error_messages/locales/en-US/session.ftl4
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs21
-rw-r--r--compiler/rustc_infer/src/errors/mod.rs71
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs18
-rw-r--r--compiler/rustc_lint/src/errors.rs25
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs35
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs47
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs17
-rw-r--r--compiler/rustc_session/src/errors.rs41
-rw-r--r--compiler/rustc_session/src/session.rs33
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs9
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs22
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr92
17 files changed, 286 insertions, 238 deletions
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index c6c85ffa84d..17ca1aa7fe1 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -1,7 +1,4 @@
-use rustc_errors::{
-    fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay,
-    SubdiagnosticMessage,
-};
+use rustc_errors::DiagnosticArgFromDisplay;
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{symbol::Ident, Span, Symbol};
 
@@ -15,25 +12,15 @@ pub struct GenericTypeWithParentheses {
     pub sub: Option<UseAngleBrackets>,
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Subdiagnostic)]
+#[multipart_suggestion(ast_lowering::use_angle_brackets, applicability = "maybe-incorrect")]
 pub struct UseAngleBrackets {
+    #[suggestion_part(code = "<")]
     pub open_param: Span,
+    #[suggestion_part(code = ">")]
     pub close_param: Span,
 }
 
-impl AddToDiagnostic for UseAngleBrackets {
-    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
-    where
-        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
-    {
-        diag.multipart_suggestion(
-            fluent::ast_lowering::use_angle_brackets,
-            vec![(self.open_param, String::from("<")), (self.close_param, String::from(">"))],
-            Applicability::MaybeIncorrect,
-        );
-    }
-}
-
 #[derive(Diagnostic)]
 #[diag(ast_lowering::invalid_abi, code = "E0703")]
 #[note]
@@ -68,30 +55,20 @@ pub struct AssocTyParentheses {
     pub sub: AssocTyParenthesesSub,
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Subdiagnostic)]
 pub enum AssocTyParenthesesSub {
-    Empty { parentheses_span: Span },
-    NotEmpty { open_param: Span, close_param: Span },
-}
-
-impl AddToDiagnostic for AssocTyParenthesesSub {
-    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
-    where
-        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
-    {
-        match self {
-            Self::Empty { parentheses_span } => diag.multipart_suggestion(
-                fluent::ast_lowering::remove_parentheses,
-                vec![(parentheses_span, String::new())],
-                Applicability::MaybeIncorrect,
-            ),
-            Self::NotEmpty { open_param, close_param } => diag.multipart_suggestion(
-                fluent::ast_lowering::use_angle_brackets,
-                vec![(open_param, String::from("<")), (close_param, String::from(">"))],
-                Applicability::MaybeIncorrect,
-            ),
-        };
-    }
+    #[multipart_suggestion(ast_lowering::remove_parentheses)]
+    Empty {
+        #[suggestion_part(code = "")]
+        parentheses_span: Span,
+    },
+    #[multipart_suggestion(ast_lowering::use_angle_brackets)]
+    NotEmpty {
+        #[suggestion_part(code = "<")]
+        open_param: Span,
+        #[suggestion_part(code = ">")]
+        close_param: Span,
+    },
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index f42aca685f6..7865c523f75 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -14,6 +14,7 @@ use rustc_ast::*;
 use rustc_ast_pretty::pprust::{self, State};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{error_code, fluent, pluralize, struct_span_err, Applicability};
+use rustc_macros::Subdiagnostic;
 use rustc_parse::validate_attr;
 use rustc_session::lint::builtin::{
     DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, PATTERNS_IN_FNS_WITHOUT_BODY,
@@ -1805,15 +1806,17 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
 }
 
 /// Used to forbid `let` expressions in certain syntactic locations.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Subdiagnostic)]
 pub(crate) enum ForbiddenLetReason {
     /// `let` is not valid and the source environment is not important
     GenericForbidden,
     /// A let chain with the `||` operator
-    NotSupportedOr(Span),
+    #[note(ast_passes::not_supported_or)]
+    NotSupportedOr(#[primary_span] Span),
     /// A let chain with invalid parentheses
     ///
     /// For example, `let 1 = 1 && (expr && expr)` is allowed
     /// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
-    NotSupportedParentheses(Span),
+    #[note(ast_passes::not_supported_parentheses)]
+    NotSupportedParentheses(#[primary_span] Span),
 }
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index ba2ed24fc08..8319352bd24 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -16,23 +16,6 @@ pub struct ForbiddenLet {
     pub(crate) reason: ForbiddenLetReason,
 }
 
-impl AddToDiagnostic for ForbiddenLetReason {
-    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
-    where
-        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
-    {
-        match self {
-            Self::GenericForbidden => {}
-            Self::NotSupportedOr(span) => {
-                diag.span_note(span, fluent::ast_passes::not_supported_or);
-            }
-            Self::NotSupportedParentheses(span) => {
-                diag.span_note(span, fluent::ast_passes::not_supported_parentheses);
-            }
-        }
-    }
-}
-
 #[derive(Diagnostic)]
 #[diag(ast_passes::forbidden_let_stable)]
 #[note]
diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl
index 65371a28591..18b3408b06a 100644
--- a/compiler/rustc_error_messages/locales/en-US/infer.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl
@@ -164,7 +164,9 @@ infer_region_explanation = {$pref_kind ->
 }
 
 infer_mismatched_static_lifetime = incompatible lifetime on type
-infer_msl_impl_note = ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
+infer_does_not_outlive_static_from_impl = ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
+infer_implicit_static_lifetime_note = this has an implicit `'static` lifetime requirement
+infer_implicit_static_lifetime_suggestion = consider relaxing the implicit `'static` requirement
 infer_msl_introduces_static = introduces a `'static` lifetime requirement
 infer_msl_unmet_req = because this has an unmet lifetime requirement
 infer_msl_trait_note = this has an implicit `'static` lifetime requirement
diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl
index 47127ea8e9c..e2277923072 100644
--- a/compiler/rustc_error_messages/locales/en-US/session.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/session.ftl
@@ -54,3 +54,7 @@ session_crate_name_empty = crate name must not be empty
 session_invalid_character_in_create_name = invalid character `{$character}` in crate name: `{$crate_name}`
 
 session_expr_parentheses_needed = parentheses are required to parse this as an expression
+
+session_skipping_const_checks = skipping const checks
+session_unleashed_feature_help_named = skipping check for `{$gate}` feature
+session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 860f24871bc..571b7dbfd1b 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -1,14 +1,14 @@
 use crate::{
     fluent, DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg,
 };
-use rustc_target::abi::TargetDataLayoutErrors;
-use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple};
-
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_hir as hir;
+use rustc_lint_defs::Level;
 use rustc_span::edition::Edition;
 use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol};
+use rustc_target::abi::TargetDataLayoutErrors;
+use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple};
 use std::borrow::Cow;
 use std::fmt;
 use std::num::ParseIntError;
@@ -155,6 +155,21 @@ impl IntoDiagnosticArg for ast::token::TokenKind {
     }
 }
 
+impl IntoDiagnosticArg for Level {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::Borrowed(match self {
+            Level::Allow => "-A",
+            Level::Warn => "-W",
+            Level::ForceWarn(_) => "--force-warn",
+            Level::Deny => "-D",
+            Level::Forbid => "-F",
+            Level::Expect(_) => {
+                unreachable!("lints with the level of `expect` should not run this code");
+            }
+        }))
+    }
+}
+
 impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
     fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
         let mut diag;
diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs
index 500900d3d4a..25e8630bf6b 100644
--- a/compiler/rustc_infer/src/errors/mod.rs
+++ b/compiler/rustc_infer/src/errors/mod.rs
@@ -459,47 +459,34 @@ impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq {
     }
 }
 
-pub struct ImplNote {
-    pub impl_span: Option<Span>,
-}
-
-impl AddToDiagnostic for ImplNote {
-    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
-    where
-        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
-    {
-        match self.impl_span {
-            Some(span) => diag.span_note(span, fluent::infer::msl_impl_note),
-            None => diag.note(fluent::infer::msl_impl_note),
-        };
-    }
-}
-
-pub enum TraitSubdiag {
-    Note { span: Span },
-    Sugg { span: Span },
+// FIXME(#100717): replace with a `Option<Span>` when subdiagnostic supports that
+#[derive(Subdiagnostic)]
+pub enum DoesNotOutliveStaticFromImpl {
+    #[note(infer::does_not_outlive_static_from_impl)]
+    Spanned {
+        #[primary_span]
+        span: Span,
+    },
+    #[note(infer::does_not_outlive_static_from_impl)]
+    Unspanned,
 }
 
-// FIXME(#100717) used in `Vec<TraitSubdiag>` so requires eager translation/list support
-impl AddToDiagnostic for TraitSubdiag {
-    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
-    where
-        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
-    {
-        match self {
-            TraitSubdiag::Note { span } => {
-                diag.span_note(span, "this has an implicit `'static` lifetime requirement");
-            }
-            TraitSubdiag::Sugg { span } => {
-                diag.span_suggestion_verbose(
-                    span,
-                    "consider relaxing the implicit `'static` requirement",
-                    " + '_".to_owned(),
-                    rustc_errors::Applicability::MaybeIncorrect,
-                );
-            }
-        }
-    }
+#[derive(Subdiagnostic)]
+pub enum ImplicitStaticLifetimeSubdiag {
+    #[note(infer::implicit_static_lifetime_note)]
+    Note {
+        #[primary_span]
+        span: Span,
+    },
+    #[suggestion_verbose(
+        infer::implicit_static_lifetime_suggestion,
+        code = " + '_",
+        applicability = "maybe-incorrect"
+    )]
+    Sugg {
+        #[primary_span]
+        span: Span,
+    },
 }
 
 #[derive(Diagnostic)]
@@ -512,7 +499,7 @@ pub struct MismatchedStaticLifetime<'a> {
     #[subdiagnostic]
     pub expl: Option<note_and_explain::RegionExplanation<'a>>,
     #[subdiagnostic]
-    pub impl_note: ImplNote,
-    #[subdiagnostic]
-    pub trait_subdiags: Vec<TraitSubdiag>,
+    pub does_not_outlive_static_from_impl: DoesNotOutliveStaticFromImpl,
+    #[subdiagnostic(eager)]
+    pub implicit_static_lifetimes: Vec<ImplicitStaticLifetimeSubdiag>,
 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
index 1410e2b63b0..c5f2a1a3f7d 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs
@@ -2,7 +2,9 @@
 //! to hold.
 
 use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq};
-use crate::errors::{ImplNote, MismatchedStaticLifetime, TraitSubdiag};
+use crate::errors::{
+    DoesNotOutliveStaticFromImpl, ImplicitStaticLifetimeSubdiag, MismatchedStaticLifetime,
+};
 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
 use crate::infer::lexical_region_resolve::RegionResolutionError;
 use crate::infer::{SubregionOrigin, TypeTrace};
@@ -56,7 +58,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             note_and_explain::SuffixKind::Continues,
         );
         let mut impl_span = None;
-        let mut trait_subdiags = Vec::new();
+        let mut implicit_static_lifetimes = Vec::new();
         if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
             // If an impl is local, then maybe this isn't what they want. Try to
             // be as helpful as possible with implicit lifetimes.
@@ -90,10 +92,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                 // Otherwise, point at all implicit static lifetimes
 
                 for span in &traits {
-                    trait_subdiags.push(TraitSubdiag::Note { span: *span });
+                    implicit_static_lifetimes
+                        .push(ImplicitStaticLifetimeSubdiag::Note { span: *span });
                     // It would be nice to put this immediately under the above note, but they get
                     // pushed to the end.
-                    trait_subdiags.push(TraitSubdiag::Sugg { span: span.shrink_to_hi() });
+                    implicit_static_lifetimes
+                        .push(ImplicitStaticLifetimeSubdiag::Sugg { span: span.shrink_to_hi() });
                 }
             }
         } else {
@@ -105,8 +109,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             cause_span: cause.span,
             unmet_lifetime_reqs: multispan_subdiag,
             expl,
-            impl_note: ImplNote { impl_span },
-            trait_subdiags,
+            does_not_outlive_static_from_impl: impl_span
+                .map(|span| DoesNotOutliveStaticFromImpl::Spanned { span })
+                .unwrap_or(DoesNotOutliveStaticFromImpl::Unspanned),
+            implicit_static_lifetimes,
         };
         let reported = self.tcx().sess.emit_err(err);
         Some(reported)
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index 97d012fb611..34b70030a58 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -88,34 +88,13 @@ pub struct BuiltinEllpisisInclusiveRangePatterns {
     pub replace: String,
 }
 
+#[derive(Subdiagnostic)]
+#[note(lint::requested_level)]
 pub struct RequestedLevel {
     pub level: Level,
     pub lint_name: String,
 }
 
-impl AddToDiagnostic for RequestedLevel {
-    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
-    where
-        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
-    {
-        diag.note(fluent::lint::requested_level);
-        diag.set_arg(
-            "level",
-            match self.level {
-                Level::Allow => "-A",
-                Level::Warn => "-W",
-                Level::ForceWarn(_) => "--force-warn",
-                Level::Deny => "-D",
-                Level::Forbid => "-F",
-                Level::Expect(_) => {
-                    unreachable!("lints with the level of `expect` should not run this code");
-                }
-            },
-        );
-        diag.set_arg("lint_name", self.lint_name);
-    }
-}
-
 #[derive(Diagnostic)]
 #[diag(lint::unsupported_group, code = "E0602")]
 pub struct UnsupportedGroup {
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index dcbe89251cb..9f7d2661a3e 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -5,7 +5,7 @@ use crate::diagnostics::error::{
     DiagnosticDeriveError,
 };
 use crate::diagnostics::utils::{
-    build_field_mapping, report_error_if_not_applied_to_span, report_type_error,
+    build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error,
     should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, FieldInnerTy, FieldMap,
     HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
 };
@@ -152,8 +152,12 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
     fn parse_subdiag_attribute(
         &self,
         attr: &Attribute,
-    ) -> Result<(SubdiagnosticKind, Path), DiagnosticDeriveError> {
-        let (subdiag, slug) = SubdiagnosticKind::from_attr(attr, self)?;
+    ) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
+        let Some((subdiag, slug)) = SubdiagnosticKind::from_attr(attr, self)? else {
+            // Some attributes aren't errors - like documentation comments - but also aren't
+            // subdiagnostics.
+            return Ok(None);
+        };
 
         if let SubdiagnosticKind::MultipartSuggestion { .. } = subdiag {
             let meta = attr.parse_meta()?;
@@ -170,7 +174,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
             SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(),
         });
 
-        Ok((subdiag, slug))
+        Ok(Some((subdiag, slug)))
     }
 
     /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
@@ -182,6 +186,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
     ) -> Result<TokenStream, DiagnosticDeriveError> {
         let diag = &self.parent.diag;
 
+        // Always allow documentation comments.
+        if is_doc_comment(attr) {
+            return Ok(quote! {});
+        }
+
         let name = attr.path.segments.last().unwrap().ident.to_string();
         let name = name.as_str();
         let meta = attr.parse_meta()?;
@@ -250,7 +259,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
             return Ok(tokens);
         }
 
-        let (subdiag, slug) = self.parse_subdiag_attribute(attr)?;
+        let Some((subdiag, slug)) = self.parse_subdiag_attribute(attr)? else {
+            // Some attributes aren't errors - like documentation comments - but also aren't
+            // subdiagnostics.
+            return Ok(quote! {});
+        };
         let fn_ident = format_ident!("{}", subdiag);
         match subdiag {
             SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => {
@@ -291,6 +304,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
             .attrs
             .iter()
             .map(move |attr| {
+                // Always allow documentation comments.
+                if is_doc_comment(attr) {
+                    return quote! {};
+                }
+
                 let name = attr.path.segments.last().unwrap().ident.to_string();
                 let needs_clone =
                     name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_));
@@ -397,8 +415,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
             _ => (),
         }
 
-        let (subdiag, slug) = self.parse_subdiag_attribute(attr)?;
-
+        let Some((subdiag, slug)) = self.parse_subdiag_attribute(attr)? else {
+            // Some attributes aren't errors - like documentation comments - but also aren't
+            // subdiagnostics.
+            return Ok(quote! {});
+        };
         let fn_ident = format_ident!("{}", subdiag);
         match subdiag {
             SubdiagnosticKind::Label => {
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 3d4c3ab9fd7..d1acb713842 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -5,9 +5,9 @@ use crate::diagnostics::error::{
     DiagnosticDeriveError,
 };
 use crate::diagnostics::utils::{
-    build_field_mapping, new_code_ident, report_error_if_not_applied_to_applicability,
-    report_error_if_not_applied_to_span, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce,
-    SpannedOption, SubdiagnosticKind,
+    build_field_mapping, is_doc_comment, new_code_ident,
+    report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, FieldInfo,
+    FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
 };
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
@@ -41,8 +41,14 @@ impl SubdiagnosticDeriveBuilder {
                 }
             }
 
-            if matches!(ast.data, syn::Data::Enum(..)) {
+            let is_enum = matches!(ast.data, syn::Data::Enum(..));
+            if is_enum {
                 for attr in &ast.attrs {
+                    // Always allow documentation comments.
+                    if is_doc_comment(attr) {
+                        continue;
+                    }
+
                     span_err(
                         attr.span().unwrap(),
                         "unsupported type attribute for subdiagnostic enum",
@@ -62,6 +68,7 @@ impl SubdiagnosticDeriveBuilder {
                     span_field: None,
                     applicability: None,
                     has_suggestion_parts: false,
+                    is_enum,
                 };
                 builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
             });
@@ -79,7 +86,7 @@ impl SubdiagnosticDeriveBuilder {
             gen impl rustc_errors::AddToDiagnostic for @Self {
                 fn add_to_diagnostic_with<__F>(self, #diag: &mut rustc_errors::Diagnostic, #f: __F)
                 where
-                    __F: Fn(
+                    __F: core::ops::Fn(
                         &mut rustc_errors::Diagnostic,
                         rustc_errors::SubdiagnosticMessage
                     ) -> rustc_errors::SubdiagnosticMessage,
@@ -122,6 +129,9 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
     /// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
     /// during finalization if still `false`.
     has_suggestion_parts: bool,
+
+    /// Set to true when this variant is an enum variant rather than just the body of a struct.
+    is_enum: bool,
 }
 
 impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
@@ -173,7 +183,11 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
         let mut kind_slugs = vec![];
 
         for attr in self.variant.ast().attrs {
-            let (kind, slug) = SubdiagnosticKind::from_attr(attr, self)?;
+            let Some((kind, slug)) = SubdiagnosticKind::from_attr(attr, self)? else {
+                // Some attributes aren't errors - like documentation comments - but also aren't
+                // subdiagnostics.
+                continue;
+            };
 
             let Some(slug) = slug else {
                 let name = attr.path.segments.last().unwrap().ident.to_string();
@@ -227,6 +241,11 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
         ast.attrs
             .iter()
             .map(|attr| {
+                // Always allow documentation comments.
+                if is_doc_comment(attr) {
+                    return quote! {};
+                }
+
                 let info = FieldInfo {
                     binding,
                     ty: inner_ty.inner_type().unwrap_or(&ast.ty),
@@ -290,6 +309,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
                     report_error_if_not_applied_to_span(attr, &info)?;
 
                     let binding = info.binding.binding.clone();
+                    // FIXME(#100717): support `Option<Span>` on `primary_span` like in the
+                    // diagnostic derive
                     self.span_field.set_once(binding, span);
                 }
 
@@ -443,10 +464,16 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
     pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
         let kind_slugs = self.identify_kind()?;
         if kind_slugs.is_empty() {
-            throw_span_err!(
-                self.variant.ast().ident.span().unwrap(),
-                "subdiagnostic kind not specified"
-            );
+            if self.is_enum {
+                // It's okay for a variant to not be a subdiagnostic at all..
+                return Ok(quote! {});
+            } else {
+                // ..but structs should always be _something_.
+                throw_span_err!(
+                    self.variant.ast().ident.span().unwrap(),
+                    "subdiagnostic kind not specified"
+                );
+            }
         };
 
         let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect();
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 4fd4adc5112..61d5007fc30 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -477,7 +477,12 @@ impl SubdiagnosticKind {
     pub(super) fn from_attr(
         attr: &Attribute,
         fields: &impl HasFieldMap,
-    ) -> Result<(SubdiagnosticKind, Option<Path>), DiagnosticDeriveError> {
+    ) -> Result<Option<(SubdiagnosticKind, Option<Path>)>, DiagnosticDeriveError> {
+        // Always allow documentation comments.
+        if is_doc_comment(attr) {
+            return Ok(None);
+        }
+
         let span = attr.span().unwrap();
 
         let name = attr.path.segments.last().unwrap().ident.to_string();
@@ -526,7 +531,9 @@ impl SubdiagnosticKind {
                     | SubdiagnosticKind::Note
                     | SubdiagnosticKind::Help
                     | SubdiagnosticKind::Warn
-                    | SubdiagnosticKind::MultipartSuggestion { .. } => return Ok((kind, None)),
+                    | SubdiagnosticKind::MultipartSuggestion { .. } => {
+                        return Ok(Some((kind, None)));
+                    }
                     SubdiagnosticKind::Suggestion { .. } => {
                         throw_span_err!(span, "suggestion without `code = \"...\"`")
                     }
@@ -626,7 +633,7 @@ impl SubdiagnosticKind {
             | SubdiagnosticKind::MultipartSuggestion { .. } => {}
         }
 
-        Ok((kind, slug))
+        Ok(Some((kind, slug)))
     }
 }
 
@@ -654,3 +661,7 @@ impl quote::IdentFragment for SubdiagnosticKind {
 pub(super) fn should_generate_set_arg(field: &Field) -> bool {
     field.attrs.is_empty()
 }
+
+pub(super) fn is_doc_comment(attr: &Attribute) -> bool {
+    attr.path.segments.last().unwrap().ident.to_string() == "doc"
+}
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index d12796f289e..500ab4a584c 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -1,9 +1,7 @@
 use std::num::NonZeroU32;
 
 use crate::cgu_reuse_tracker::CguReuse;
-use rustc_errors::{
-    fluent, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, MultiSpan,
-};
+use rustc_errors::MultiSpan;
 use rustc_macros::Diagnostic;
 use rustc_span::{Span, Symbol};
 use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple};
@@ -148,24 +146,15 @@ pub struct CrateNameEmpty {
     pub span: Option<Span>,
 }
 
+#[derive(Diagnostic)]
+#[diag(session::invalid_character_in_create_name)]
 pub struct InvalidCharacterInCrateName<'a> {
+    #[primary_span]
     pub span: Option<Span>,
     pub character: char,
     pub crate_name: &'a str,
 }
 
-impl IntoDiagnostic<'_> for InvalidCharacterInCrateName<'_> {
-    fn into_diagnostic(self, sess: &Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
-        let mut diag = sess.struct_err(fluent::session::invalid_character_in_create_name);
-        if let Some(sp) = self.span {
-            diag.set_span(sp);
-        }
-        diag.set_arg("character", self.character);
-        diag.set_arg("crate_name", self.crate_name);
-        diag
-    }
-}
-
 #[derive(Subdiagnostic)]
 #[multipart_suggestion(session::expr_parentheses_needed, applicability = "machine-applicable")]
 pub struct ExprParenthesesNeeded {
@@ -180,3 +169,25 @@ impl ExprParenthesesNeeded {
         ExprParenthesesNeeded { left: s.shrink_to_lo(), right: s.shrink_to_hi() }
     }
 }
+
+#[derive(Diagnostic)]
+#[diag(session::skipping_const_checks)]
+pub struct SkippingConstChecks {
+    #[subdiagnostic(eager)]
+    pub unleashed_features: Vec<UnleashedFeatureHelp>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum UnleashedFeatureHelp {
+    #[help(session::unleashed_feature_help_named)]
+    Named {
+        #[primary_span]
+        span: Span,
+        gate: Symbol,
+    },
+    #[help(session::unleashed_feature_help_unnamed)]
+    Unnamed {
+        #[primary_span]
+        span: Span,
+    },
+}
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index b5e25f45fa7..100c66f6364 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -5,9 +5,10 @@ use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, S
 use crate::errors::{
     CannotEnableCrtStaticLinux, CannotMixAndMatchSanitizers, LinkerPluginToWindowsNotSupported,
     NotCircumventFeature, ProfileSampleUseFileDoesNotExist, ProfileUseFileDoesNotExist,
-    SanitizerCfiEnabled, SanitizerNotSupported, SanitizersNotSupported,
+    SanitizerCfiEnabled, SanitizerNotSupported, SanitizersNotSupported, SkippingConstChecks,
     SplitDebugInfoUnstablePlatform, StackProtectorNotSupportedForTarget,
-    TargetRequiresUnwindTables, UnstableVirtualFunctionElimination, UnsupportedDwarfVersion,
+    TargetRequiresUnwindTables, UnleashedFeatureHelp, UnstableVirtualFunctionElimination,
+    UnsupportedDwarfVersion,
 };
 use crate::parse::{add_feature_diagnostics, ParseSess};
 use crate::search_paths::{PathKind, SearchPath};
@@ -232,21 +233,19 @@ impl Session {
         if !unleashed_features.is_empty() {
             let mut must_err = false;
             // Create a diagnostic pointing at where things got unleashed.
-            // FIXME(#100717): needs eager translation/lists
-            #[allow(rustc::untranslatable_diagnostic)]
-            #[allow(rustc::diagnostic_outside_of_impl)]
-            let mut diag = self.struct_warn("skipping const checks");
-            for &(span, feature_gate) in unleashed_features.iter() {
-                // FIXME: `span_label` doesn't do anything, so we use "help" as a hack.
-                if let Some(gate) = feature_gate {
-                    diag.span_help(span, &format!("skipping check for `{gate}` feature"));
-                    // The unleash flag must *not* be used to just "hack around" feature gates.
-                    must_err = true;
-                } else {
-                    diag.span_help(span, "skipping check that does not even have a feature gate");
-                }
-            }
-            diag.emit();
+            self.emit_warning(SkippingConstChecks {
+                unleashed_features: unleashed_features
+                    .iter()
+                    .map(|(span, gate)| {
+                        gate.map(|gate| {
+                            must_err = true;
+                            UnleashedFeatureHelp::Named { span: *span, gate }
+                        })
+                        .unwrap_or(UnleashedFeatureHelp::Unnamed { span: *span })
+                    })
+                    .collect(),
+            });
+
             // If we should err, make sure we did.
             if must_err && self.has_errors().is_none() {
                 // We have skipped a feature gate, and not run into other errors... reject.
diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
index e873c36e0b3..b8deb48d49a 100644
--- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
@@ -749,3 +749,12 @@ struct SubdiagnosticEagerSuggestion {
     #[subdiagnostic(eager)]
     sub: SubdiagnosticWithSuggestion,
 }
+
+/// with a doc comment on the type..
+#[derive(Diagnostic)]
+#[diag(compiletest::example, code = "E0123")]
+struct WithDocComment {
+    /// ..and the field
+    #[primary_span]
+    span: Span,
+}
diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
index 84ee5af42de..2fe93d46d0a 100644
--- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs
@@ -237,7 +237,6 @@ enum V {
         var: String,
     },
     B {
-    //~^ ERROR subdiagnostic kind not specified
         #[primary_span]
         span: Span,
         var: String,
@@ -641,3 +640,24 @@ struct BJ {
     span: Span,
     r#type: String,
 }
+
+/// with a doc comment on the type..
+#[derive(Subdiagnostic)]
+#[label(parser::add_paren)]
+struct BK {
+    /// ..and the field
+    #[primary_span]
+    span: Span,
+}
+
+/// with a doc comment on the type..
+#[derive(Subdiagnostic)]
+enum BL {
+    /// ..and the variant..
+    #[label(parser::add_paren)]
+    Foo {
+        /// ..and the field
+        #[primary_span]
+        span: Span,
+    }
+}
diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
index 171b89e657d..1173d24248b 100644
--- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
+++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr
@@ -134,20 +134,14 @@ error: diagnostic slug must be first argument of a `#[label(...)]` attribute
 LL |     #[label(code = "...")]
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error: subdiagnostic kind not specified
-  --> $DIR/subdiagnostic-derive.rs:239:5
-   |
-LL |     B {
-   |     ^
-
 error: the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan`
-  --> $DIR/subdiagnostic-derive.rs:251:5
+  --> $DIR/subdiagnostic-derive.rs:250:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
 
 error: label without `#[primary_span]` field
-  --> $DIR/subdiagnostic-derive.rs:248:1
+  --> $DIR/subdiagnostic-derive.rs:247:1
    |
 LL | / #[label(parser::add_paren)]
 LL | |
@@ -159,13 +153,13 @@ LL | | }
    | |_^
 
 error: `#[applicability]` is only valid on suggestions
-  --> $DIR/subdiagnostic-derive.rs:261:5
+  --> $DIR/subdiagnostic-derive.rs:260:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
 
 error: `#[bar]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:271:5
+  --> $DIR/subdiagnostic-derive.rs:270:5
    |
 LL |     #[bar]
    |     ^^^^^^
@@ -173,13 +167,13 @@ LL |     #[bar]
    = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
 
 error: `#[bar = ...]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:282:5
+  --> $DIR/subdiagnostic-derive.rs:281:5
    |
 LL |     #[bar = "..."]
    |     ^^^^^^^^^^^^^^
 
 error: `#[bar(...)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:293:5
+  --> $DIR/subdiagnostic-derive.rs:292:5
    |
 LL |     #[bar("...")]
    |     ^^^^^^^^^^^^^
@@ -187,7 +181,7 @@ LL |     #[bar("...")]
    = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
 
 error: unexpected unsupported untagged union
-  --> $DIR/subdiagnostic-derive.rs:309:1
+  --> $DIR/subdiagnostic-derive.rs:308:1
    |
 LL | / union AC {
 LL | |
@@ -197,7 +191,7 @@ LL | | }
    | |_^
 
 error: `#[label(parser::add_paren)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:324:28
+  --> $DIR/subdiagnostic-derive.rs:323:28
    |
 LL | #[label(parser::add_paren, parser::add_paren)]
    |                            ^^^^^^^^^^^^^^^^^
@@ -205,67 +199,67 @@ LL | #[label(parser::add_paren, parser::add_paren)]
    = help: a diagnostic slug must be the first argument to the attribute
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:337:5
+  --> $DIR/subdiagnostic-derive.rs:336:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:334:5
+  --> $DIR/subdiagnostic-derive.rs:333:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
 
 error: subdiagnostic kind not specified
-  --> $DIR/subdiagnostic-derive.rs:343:8
+  --> $DIR/subdiagnostic-derive.rs:342:8
    |
 LL | struct AG {
    |        ^^
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:380:47
+  --> $DIR/subdiagnostic-derive.rs:379:47
    |
 LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
    |                                               ^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:380:33
+  --> $DIR/subdiagnostic-derive.rs:379:33
    |
 LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
    |                                 ^^^^^^^^^^^^
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:398:5
+  --> $DIR/subdiagnostic-derive.rs:397:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:395:5
+  --> $DIR/subdiagnostic-derive.rs:394:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
 
 error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
-  --> $DIR/subdiagnostic-derive.rs:408:5
+  --> $DIR/subdiagnostic-derive.rs:407:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
 
 error: suggestion without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:421:1
+  --> $DIR/subdiagnostic-derive.rs:420:1
    |
 LL | #[suggestion(parser::add_paren)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: invalid applicability
-  --> $DIR/subdiagnostic-derive.rs:431:46
+  --> $DIR/subdiagnostic-derive.rs:430:46
    |
 LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
    |                                              ^^^^^^^^^^^^^^^^^^^^^
 
 error: suggestion without `#[primary_span]` field
-  --> $DIR/subdiagnostic-derive.rs:449:1
+  --> $DIR/subdiagnostic-derive.rs:448:1
    |
 LL | / #[suggestion(parser::add_paren, code = "...")]
 LL | |
@@ -275,25 +269,25 @@ LL | | }
    | |_^
 
 error: unsupported type attribute for subdiagnostic enum
-  --> $DIR/subdiagnostic-derive.rs:463:1
+  --> $DIR/subdiagnostic-derive.rs:462:1
    |
 LL | #[label]
    | ^^^^^^^^
 
 error: `var` doesn't refer to a field on this type
-  --> $DIR/subdiagnostic-derive.rs:483:39
+  --> $DIR/subdiagnostic-derive.rs:482:39
    |
 LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
    |                                       ^^^^^^^
 
 error: `var` doesn't refer to a field on this type
-  --> $DIR/subdiagnostic-derive.rs:502:43
+  --> $DIR/subdiagnostic-derive.rs:501:43
    |
 LL |     #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
    |                                           ^^^^^^^
 
 error: `#[suggestion_part]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:525:5
+  --> $DIR/subdiagnostic-derive.rs:524:5
    |
 LL |     #[suggestion_part]
    |     ^^^^^^^^^^^^^^^^^^
@@ -301,7 +295,7 @@ LL |     #[suggestion_part]
    = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead
 
 error: `#[suggestion_part(...)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:528:5
+  --> $DIR/subdiagnostic-derive.rs:527:5
    |
 LL |     #[suggestion_part(code = "...")]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -309,7 +303,7 @@ LL |     #[suggestion_part(code = "...")]
    = help: `#[suggestion_part(...)]` is only valid in multipart suggestions
 
 error: suggestion without `#[primary_span]` field
-  --> $DIR/subdiagnostic-derive.rs:522:1
+  --> $DIR/subdiagnostic-derive.rs:521:1
    |
 LL | / #[suggestion(parser::add_paren, code = "...")]
 LL | |
@@ -321,7 +315,7 @@ LL | | }
    | |_^
 
 error: `#[multipart_suggestion(code = ...)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:537:43
+  --> $DIR/subdiagnostic-derive.rs:536:43
    |
 LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
    |                                           ^^^^^^^^^^^^
@@ -329,7 +323,7 @@ LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "ma
    = help: only `applicability` is a valid nested attributes
 
 error: multipart suggestion without any `#[suggestion_part(...)]` fields
-  --> $DIR/subdiagnostic-derive.rs:537:1
+  --> $DIR/subdiagnostic-derive.rs:536:1
    |
 LL | / #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
 LL | |
@@ -340,19 +334,19 @@ LL | | }
    | |_^
 
 error: `#[suggestion_part(...)]` attribute without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:547:5
+  --> $DIR/subdiagnostic-derive.rs:546:5
    |
 LL |     #[suggestion_part]
    |     ^^^^^^^^^^^^^^^^^^
 
 error: `#[suggestion_part(...)]` attribute without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:555:5
+  --> $DIR/subdiagnostic-derive.rs:554:5
    |
 LL |     #[suggestion_part()]
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: `#[primary_span]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:564:5
+  --> $DIR/subdiagnostic-derive.rs:563:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
@@ -360,7 +354,7 @@ LL |     #[primary_span]
    = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]`
 
 error: multipart suggestion without any `#[suggestion_part(...)]` fields
-  --> $DIR/subdiagnostic-derive.rs:561:1
+  --> $DIR/subdiagnostic-derive.rs:560:1
    |
 LL | / #[multipart_suggestion(parser::add_paren)]
 LL | |
@@ -372,19 +366,19 @@ LL | | }
    | |_^
 
 error: `#[suggestion_part(...)]` attribute without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:572:5
+  --> $DIR/subdiagnostic-derive.rs:571:5
    |
 LL |     #[suggestion_part]
    |     ^^^^^^^^^^^^^^^^^^
 
 error: `#[suggestion_part(...)]` attribute without `code = "..."`
-  --> $DIR/subdiagnostic-derive.rs:575:5
+  --> $DIR/subdiagnostic-derive.rs:574:5
    |
 LL |     #[suggestion_part()]
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: `#[suggestion_part(foo = ...)]` is not a valid attribute
-  --> $DIR/subdiagnostic-derive.rs:578:23
+  --> $DIR/subdiagnostic-derive.rs:577:23
    |
 LL |     #[suggestion_part(foo = "bar")]
    |                       ^^^^^^^^^^^
@@ -392,31 +386,31 @@ LL |     #[suggestion_part(foo = "bar")]
    = help: `code` is the only valid nested attribute
 
 error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
-  --> $DIR/subdiagnostic-derive.rs:581:5
+  --> $DIR/subdiagnostic-derive.rs:580:5
    |
 LL |     #[suggestion_part(code = "...")]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
-  --> $DIR/subdiagnostic-derive.rs:584:5
+  --> $DIR/subdiagnostic-derive.rs:583:5
    |
 LL |     #[suggestion_part()]
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: specified multiple times
-  --> $DIR/subdiagnostic-derive.rs:592:37
+  --> $DIR/subdiagnostic-derive.rs:591:37
    |
 LL |     #[suggestion_part(code = "...", code = ",,,")]
    |                                     ^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/subdiagnostic-derive.rs:592:23
+  --> $DIR/subdiagnostic-derive.rs:591:23
    |
 LL |     #[suggestion_part(code = "...", code = ",,,")]
    |                       ^^^^^^^^^^^^
 
 error: `#[applicability]` has no effect if all `#[suggestion]`/`#[multipart_suggestion]` attributes have a static `applicability = "..."`
-  --> $DIR/subdiagnostic-derive.rs:621:5
+  --> $DIR/subdiagnostic-derive.rs:620:5
    |
 LL |     #[applicability]
    |     ^^^^^^^^^^^^^^^^
@@ -458,19 +452,19 @@ LL |     #[bar("...")]
    |       ^^^
 
 error: cannot find attribute `bar` in this scope
-  --> $DIR/subdiagnostic-derive.rs:271:7
+  --> $DIR/subdiagnostic-derive.rs:270:7
    |
 LL |     #[bar]
    |       ^^^
 
 error: cannot find attribute `bar` in this scope
-  --> $DIR/subdiagnostic-derive.rs:282:7
+  --> $DIR/subdiagnostic-derive.rs:281:7
    |
 LL |     #[bar = "..."]
    |       ^^^
 
 error: cannot find attribute `bar` in this scope
-  --> $DIR/subdiagnostic-derive.rs:293:7
+  --> $DIR/subdiagnostic-derive.rs:292:7
    |
 LL |     #[bar("...")]
    |       ^^^
@@ -481,6 +475,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent`
 LL | #[label(slug)]
    |         ^^^^ not found in `rustc_errors::fluent`
 
-error: aborting due to 68 previous errors
+error: aborting due to 67 previous errors
 
 For more information about this error, try `rustc --explain E0425`.