Skip to content

Commit

Permalink
Auto merge of #102189 - davidtwco:translation-derive-enums, r=compile…
Browse files Browse the repository at this point in the history
…r-errors

macros: diagnostic derive on enums

Part of #100717.

Extends `#[derive(Diagnostic)]` to work on enums too where each variant acts like a distinct diagnostic - being able to represent diagnostics this way can be quite a bit simpler for some parts of the compiler.

r? `@compiler-errors`
cc `@Xiretza`
  • Loading branch information
bors committed Sep 27, 2022
2 parents cd4d9d9 + f20c882 commit de0b511
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 308 deletions.
157 changes: 54 additions & 103 deletions compiler/rustc_macros/src/diagnostics/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
}
Expand All @@ -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 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
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
let preamble = builder.preamble(&variant);
let body = builder.body(&variant);

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();
}
Some(slug) => {
quote! {
let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug);
}
#diag
}
} else {
span_err(
ast.span().unwrap(),
"`#[derive(Diagnostic)]` can only be used on structs",
)
.emit();
};

DiagnosticDeriveError::ErrorHandled.to_compile_error()
quote! {
#init
#preamble
#body
#diag
}
};
});

structure.gen_impl(quote! {
gen impl<'__diagnostic_handler_sess, G>
Expand Down Expand Up @@ -107,68 +83,43 @@ 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,
}
}

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 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();
}
Some(slug) => {
quote! {
let mut #diag = #diag.build(rustc_errors::fluent::#slug);
}
}
};

let implementation = quote! {
#init
#preamble
match self {
#attrs
}
match self {
#args
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
let preamble = builder.preamble(&variant);
let body = builder.body(&variant);

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);
}
#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! {
Expand Down
Loading

0 comments on commit de0b511

Please sign in to comment.