From 828398e986d011bbe4a16ae019fc7435c5fdf8dd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 4 Mar 2026 13:41:36 +0100 Subject: [PATCH] Remove `LintDiagnostic` derive proc-macro --- .../src/diagnostics/diagnostic.rs | 54 +------- .../src/diagnostics/diagnostic_builder.rs | 125 +++++++----------- compiler/rustc_macros/src/diagnostics/mod.rs | 36 +---- compiler/rustc_macros/src/lib.rs | 19 --- 4 files changed, 52 insertions(+), 182 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index dc8231e5f0b0..2f7c3cc6a46d 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -4,7 +4,7 @@ use quote::quote; use synstructure::Structure; -use crate::diagnostics::diagnostic_builder::DiagnosticDeriveKind; +use crate::diagnostics::diagnostic_builder::each_variant; use crate::diagnostics::error::DiagnosticDeriveError; /// The central struct for constructing the `into_diag` method from an annotated struct. @@ -19,8 +19,7 @@ pub(crate) fn new(structure: Structure<'a>) -> Self { pub(crate) fn into_tokens(self) -> TokenStream { let DiagnosticDerive { mut structure } = self; - let kind = DiagnosticDeriveKind::Diagnostic; - let implementation = kind.each_variant(&mut structure, |mut builder, variant| { + let implementation = each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); @@ -64,52 +63,3 @@ fn into_diag( }) } } - -/// The central struct for constructing the `decorate_lint` method from an annotated struct. -pub(crate) struct LintDiagnosticDerive<'a> { - structure: Structure<'a>, -} - -impl<'a> LintDiagnosticDerive<'a> { - pub(crate) fn new(structure: Structure<'a>) -> Self { - Self { structure } - } - - pub(crate) fn into_tokens(self) -> TokenStream { - let LintDiagnosticDerive { mut structure } = self; - let kind = DiagnosticDeriveKind::LintDiagnostic; - let implementation = kind.each_variant(&mut structure, |mut builder, variant| { - let preamble = builder.preamble(variant); - let body = builder.body(variant); - - let Some(message) = builder.primary_message() else { - return DiagnosticDeriveError::ErrorHandled.to_compile_error(); - }; - let message = message.diag_message(Some(variant)); - let primary_message = quote! { - diag.primary_message(#message); - }; - - let formatting_init = &builder.formatting_init; - quote! { - #primary_message - #preamble - #formatting_init - #body - diag - } - }); - - structure.gen_impl(quote! { - gen impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for @Self { - #[track_caller] - fn decorate_lint<'__b>( - self, - diag: &'__b mut rustc_errors::Diag<'__a, ()> - ) { - #implementation; - } - } - }) - } -} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index de8ee42caf45..e335037f2c4c 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -18,20 +18,54 @@ should_generate_arg, type_is_bool, type_is_unit, type_matches_path, }; -/// What kind of diagnostic is being derived - a fatal/error/warning or a lint? -#[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) enum DiagnosticDeriveKind { - Diagnostic, - LintDiagnostic, +pub(crate) fn each_variant<'s, F>(structure: &mut Structure<'s>, f: F) -> TokenStream +where + F: for<'v> Fn(DiagnosticDeriveVariantBuilder, &VariantInfo<'v>) -> TokenStream, +{ + let ast = structure.ast(); + let span = ast.span().unwrap(); + match ast.data { + syn::Data::Struct(..) | syn::Data::Enum(..) => (), + syn::Data::Union(..) => { + span_err(span, "diagnostic derives can only be used on structs and enums").emit(); + } + } + + if matches!(ast.data, syn::Data::Enum(..)) { + for attr in &ast.attrs { + span_err(attr.span().unwrap(), "unsupported type attribute for diagnostic derive enum") + .emit(); + } + } + + structure.bind_with(|_| synstructure::BindStyle::Move); + let variants = structure.each_variant(|variant| { + let span = match structure.ast().data { + syn::Data::Struct(..) => span, + // There isn't a good way to get the span of the variant, so the variant's + // name will need to do. + _ => variant.ast().ident.span().unwrap(), + }; + let builder = DiagnosticDeriveVariantBuilder { + span, + field_map: build_field_mapping(variant), + formatting_init: TokenStream::new(), + message: None, + code: None, + }; + f(builder, variant) + }); + + quote! { + match self { + #variants + } + } } /// Tracks persistent information required for a specific variant when building up individual calls -/// to diagnostic methods for generated diagnostic derives - both `Diagnostic` for -/// fatal/errors/warnings and `LintDiagnostic` for lints. +/// to diagnostic methods for generated diagnostic derives. pub(crate) struct DiagnosticDeriveVariantBuilder { - /// The kind for the entire type. - pub kind: DiagnosticDeriveKind, - /// Initialization of format strings for code suggestions. pub formatting_init: TokenStream, @@ -51,60 +85,6 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { pub code: SpannedOption<()>, } -impl DiagnosticDeriveKind { - /// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the - /// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions - /// or attributes on the type itself when input is an enum. - pub(crate) fn each_variant<'s, F>(self, structure: &mut Structure<'s>, f: F) -> TokenStream - where - F: for<'v> Fn(DiagnosticDeriveVariantBuilder, &VariantInfo<'v>) -> TokenStream, - { - let ast = structure.ast(); - let span = ast.span().unwrap(); - match ast.data { - syn::Data::Struct(..) | syn::Data::Enum(..) => (), - syn::Data::Union(..) => { - span_err(span, "diagnostic derives can only be used on structs and enums").emit(); - } - } - - if matches!(ast.data, syn::Data::Enum(..)) { - for attr in &ast.attrs { - span_err( - attr.span().unwrap(), - "unsupported type attribute for diagnostic derive enum", - ) - .emit(); - } - } - - structure.bind_with(|_| synstructure::BindStyle::Move); - let variants = structure.each_variant(|variant| { - let span = match structure.ast().data { - syn::Data::Struct(..) => span, - // There isn't a good way to get the span of the variant, so the variant's - // name will need to do. - _ => variant.ast().ident.span().unwrap(), - }; - let builder = DiagnosticDeriveVariantBuilder { - kind: self, - span, - field_map: build_field_mapping(variant), - formatting_init: TokenStream::new(), - message: None, - code: None, - }; - f(builder, variant) - }); - - quote! { - match self { - #variants - } - } - } -} - impl DiagnosticDeriveVariantBuilder { pub(crate) fn primary_message(&self) -> Option<&Message> { match self.message.as_ref() { @@ -358,20 +338,11 @@ fn generate_inner_field_code( // `arg` call will not be generated. (Meta::Path(_), "skip_arg") => return Ok(quote! {}), (Meta::Path(_), "primary_span") => { - match self.kind { - DiagnosticDeriveKind::Diagnostic => { - report_error_if_not_applied_to_span(attr, &info)?; + report_error_if_not_applied_to_span(attr, &info)?; - return Ok(quote! { - diag.span(#binding); - }); - } - DiagnosticDeriveKind::LintDiagnostic => { - throw_invalid_attr!(attr, |diag| { - diag.help("the `primary_span` field attribute is not valid for lint diagnostics") - }) - } - } + return Ok(quote! { + diag.span(#binding); + }); } (Meta::Path(_), "subdiagnostic") => { return Ok(quote! { diag.subdiagnostic(#binding); }); diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index c0512e86bbcb..a5828cd715a8 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -6,7 +6,7 @@ mod subdiagnostic; mod utils; -use diagnostic::{DiagnosticDerive, LintDiagnosticDerive}; +use diagnostic::DiagnosticDerive; pub(super) use msg_macro::msg_macro; use proc_macro2::TokenStream; use subdiagnostic::SubdiagnosticDerive; @@ -51,38 +51,6 @@ pub(super) fn diagnostic_derive(s: Structure<'_>) -> TokenStream { DiagnosticDerive::new(s).into_tokens() } -/// Implements `#[derive(LintDiagnostic)]`, which allows for lints to be specified as a struct, -/// independent from the actual lint emitting code. -/// -/// ```ignore (rust) -/// #[derive(LintDiagnostic)] -/// #[diag("unused attribute")] -/// pub(crate) struct UnusedAttribute { -/// #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] -/// pub this: Span, -/// #[note("attribute also specified here")] -/// pub other: Span, -/// #[warning( -/// "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" -/// )] -/// pub warning: bool, -/// } -/// ``` -/// -/// Then, later, to emit the error: -/// -/// ```ignore (rust) -/// cx.emit_span_lint(UNUSED_ATTRIBUTES, span, UnusedAttribute { -/// ... -/// }); -/// ``` -/// -/// See rustc dev guide for more examples on using the `#[derive(LintDiagnostic)]`: -/// -pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { - LintDiagnosticDerive::new(s).into_tokens() -} - /// Implements `#[derive(Subdiagnostic)]`, which allows for labels, notes, helps and /// suggestions to be specified as a structs or enums, independent from the actual diagnostics /// emitting code or diagnostic derives. @@ -99,7 +67,7 @@ pub(super) fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { /// Then, later, use the subdiagnostic in a diagnostic: /// /// ```ignore (rust) -/// #[derive(LintDiagnostic)] +/// #[derive(Diagnostic)] /// #[diag("unused doc comment")] /// pub(crate) struct BuiltinUnusedDocComment<'a> { /// pub kind: &'a str, diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index d7f75e08bac8..44969908b3f4 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -196,25 +196,6 @@ pub fn extension(attr: TokenStream, input: TokenStream) -> TokenStream { suggestion_hidden, suggestion_verbose)] => diagnostics::diagnostic_derive ); -decl_derive!( - [LintDiagnostic, attributes( - // struct attributes - diag, - help, - help_once, - note, - note_once, - warning, - // field attributes - skip_arg, - primary_span, - label, - subdiagnostic, - suggestion, - suggestion_short, - suggestion_hidden, - suggestion_verbose)] => diagnostics::lint_diagnostic_derive -); decl_derive!( [Subdiagnostic, attributes( // struct/variant attributes