Remove LintDiagnostic derive proc-macro

This commit is contained in:
Guillaume Gomez
2026-03-04 13:41:36 +01:00
parent 7ceb8ea1b0
commit 828398e986
4 changed files with 52 additions and 182 deletions
@@ -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;
}
}
})
}
}
@@ -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); });
+2 -34
View File
@@ -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)]`:
/// <https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-structs.html#reference>
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,
-19
View File
@@ -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