macros: support diagnostic derive on enums

Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
David Wood 2022-09-23 12:49:02 +01:00
parent 72f4923979
commit f20c882b8b
6 changed files with 370 additions and 307 deletions

View file

@ -2,10 +2,9 @@
use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveBuilder, DiagnosticDeriveKind};
use crate::diagnostics::error::{span_err, DiagnosticDeriveError};
use crate::diagnostics::utils::{build_field_mapping, SetOnce};
use crate::diagnostics::utils::SetOnce;
use proc_macro2::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
use synstructure::Structure;
/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
@ -18,13 +17,7 @@ pub(crate) struct DiagnosticDerive<'a> {
impl<'a> DiagnosticDerive<'a> {
pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
Self {
builder: DiagnosticDeriveBuilder {
diag,
fields: build_field_mapping(&structure),
kind: DiagnosticDeriveKind::Diagnostic,
code: None,
slug: None,
},
builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic },
handler,
structure,
}
@ -33,52 +26,35 @@ impl<'a> DiagnosticDerive<'a> {
pub(crate) fn into_tokens(self) -> TokenStream {
let DiagnosticDerive { mut structure, handler, mut builder } = self;
let ast = structure.ast();
let implementation = {
if let syn::Data::Struct(..) = ast.data {
let preamble = builder.preamble(&structure);
let (attrs, args) = builder.body(&mut structure);
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
let preamble = builder.preamble(&variant);
let body = builder.body(&variant);
let span = ast.span().unwrap();
let diag = &builder.diag;
let init = match builder.slug.value() {
None => {
span_err(span, "diagnostic slug not specified")
.help(&format!(
"specify the slug as the first argument to the `#[diag(...)]` attribute, \
such as `#[diag(typeck::example_error)]`",
))
.emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
}
Some(slug) => {
quote! {
let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug);
}
}
};
quote! {
#init
#preamble
match self {
#attrs
}
match self {
#args
}
#diag
let diag = &builder.parent.diag;
let init = match builder.slug.value_ref() {
None => {
span_err(builder.span, "diagnostic slug not specified")
.help(&format!(
"specify the slug as the first argument to the `#[diag(...)]` \
attribute, such as `#[diag(typeck::example_error)]`",
))
.emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
}
} else {
span_err(
ast.span().unwrap(),
"`#[derive(Diagnostic)]` can only be used on structs",
)
.emit();
Some(slug) => {
quote! {
let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug);
}
}
};
DiagnosticDeriveError::ErrorHandled.to_compile_error()
quote! {
#init
#preamble
#body
#diag
}
};
});
structure.gen_impl(quote! {
gen impl<'__diagnostic_handler_sess, G>
@ -107,13 +83,7 @@ pub(crate) struct LintDiagnosticDerive<'a> {
impl<'a> LintDiagnosticDerive<'a> {
pub(crate) fn new(diag: syn::Ident, structure: Structure<'a>) -> Self {
Self {
builder: DiagnosticDeriveBuilder {
diag,
fields: build_field_mapping(&structure),
kind: DiagnosticDeriveKind::LintDiagnostic,
code: None,
slug: None,
},
builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::LintDiagnostic },
structure,
}
}
@ -121,54 +91,35 @@ impl<'a> LintDiagnosticDerive<'a> {
pub(crate) fn into_tokens(self) -> TokenStream {
let LintDiagnosticDerive { mut structure, mut builder } = self;
let ast = structure.ast();
let implementation = {
if let syn::Data::Struct(..) = ast.data {
let preamble = builder.preamble(&structure);
let (attrs, args) = builder.body(&mut structure);
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
let preamble = builder.preamble(&variant);
let body = builder.body(&variant);
let diag = &builder.diag;
let span = ast.span().unwrap();
let init = match builder.slug.value() {
None => {
span_err(span, "diagnostic slug not specified")
.help(&format!(
"specify the slug as the first argument to the attribute, such as \
`#[diag(typeck::example_error)]`",
))
.emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
let diag = &builder.parent.diag;
let init = match builder.slug.value_ref() {
None => {
span_err(builder.span, "diagnostic slug not specified")
.help(&format!(
"specify the slug as the first argument to the attribute, such as \
`#[diag(typeck::example_error)]`",
))
.emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
}
Some(slug) => {
quote! {
let mut #diag = #diag.build(rustc_errors::fluent::#slug);
}
Some(slug) => {
quote! {
let mut #diag = #diag.build(rustc_errors::fluent::#slug);
}
}
};
}
};
let implementation = quote! {
#init
#preamble
match self {
#attrs
}
match self {
#args
}
#diag.emit();
};
implementation
} else {
span_err(
ast.span().unwrap(),
"`#[derive(LintDiagnostic)]` can only be used on structs",
)
.emit();
DiagnosticDeriveError::ErrorHandled.to_compile_error()
quote! {
#init
#preamble
#body
#diag.emit();
}
};
});
let diag = &builder.diag;
structure.gen_impl(quote! {