Files
rust/compiler/rustc_macros/src/diagnostics/diagnostic.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

214 lines
7.3 KiB
Rust
Raw Normal View History

#![deny(unused_must_use)]
use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveBuilder, DiagnosticDeriveKind};
use crate::diagnostics::error::{span_err, DiagnosticDeriveError};
use crate::diagnostics::utils::SetOnce;
use proc_macro2::TokenStream;
use quote::quote;
use synstructure::Structure;
/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
pub(crate) struct DiagnosticDerive<'a> {
structure: Structure<'a>,
builder: DiagnosticDeriveBuilder,
}
impl<'a> DiagnosticDerive<'a> {
pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
Self {
builder: DiagnosticDeriveBuilder {
diag,
kind: DiagnosticDeriveKind::Diagnostic { handler },
},
structure,
}
}
pub(crate) fn into_tokens(self) -> TokenStream {
let DiagnosticDerive { mut structure, mut builder } = self;
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 DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
unreachable!()
};
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(...)]` \
2022-10-22 11:07:54 +02:00
attribute, such as `#[diag(hir_analysis_example_error)]`",
))
.emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
}
Some(slug) => {
2022-11-07 19:47:32 +01:00
let check = make_check(slug);
quote! {
2022-11-07 19:47:32 +01:00
#check
let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug);
}
}
};
let formatting_init = &builder.formatting_init;
quote! {
#init
#formatting_init
#preamble
#body
#diag
}
});
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() };
structure.gen_impl(quote! {
gen impl<'__diagnostic_handler_sess, G>
rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
for @Self
where G: rustc_errors::EmissionGuarantee
{
2022-10-31 16:14:29 +01:00
#[track_caller]
fn into_diagnostic(
self,
#handler: &'__diagnostic_handler_sess rustc_errors::Handler
) -> rustc_errors::DiagnosticBuilder<'__diagnostic_handler_sess, G> {
use rustc_errors::IntoDiagnosticArg;
#implementation
}
}
})
}
}
/// The central struct for constructing the `decorate_lint` method from an annotated struct.
pub(crate) struct LintDiagnosticDerive<'a> {
structure: Structure<'a>,
builder: DiagnosticDeriveBuilder,
}
impl<'a> LintDiagnosticDerive<'a> {
pub(crate) fn new(diag: syn::Ident, structure: Structure<'a>) -> Self {
Self {
builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::LintDiagnostic },
structure,
}
}
pub(crate) fn into_tokens(self) -> TokenStream {
let LintDiagnosticDerive { mut structure, mut builder } = self;
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 formatting_init = &builder.formatting_init;
2022-09-16 11:01:02 +04:00
quote! {
#preamble
#formatting_init
2022-09-16 11:01:02 +04:00
#body
#diag
}
});
let msg = builder.each_variant(&mut structure, |mut builder, variant| {
// Collect the slug by generating the preamble.
let _ = builder.preamble(&variant);
2022-09-16 11:01:02 +04:00
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 \
2022-10-22 11:07:54 +02:00
`#[diag(compiletest_example)]`",
))
.emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
}
2022-11-07 19:47:32 +01:00
Some(slug) => {
let check = make_check(slug);
quote! {
#check
rustc_errors::fluent::#slug.into()
}
}
}
});
let diag = &builder.diag;
structure.gen_impl(quote! {
gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self {
2022-10-31 16:14:29 +01:00
#[track_caller]
fn decorate_lint<'__b>(
self,
#diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>
) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> {
use rustc_errors::IntoDiagnosticArg;
#implementation
}
2022-09-16 11:01:02 +04:00
fn msg(&self) -> rustc_errors::DiagnosticMessage {
#msg
}
}
})
}
}
2022-11-07 19:47:32 +01:00
/// Checks whether the slug starts with the crate name it's in.
fn make_check(slug: &syn::Path) -> TokenStream {
quote! {
const _: () = {
2022-11-08 00:42:00 +01:00
const krate_str: &str = match option_env!("CARGO_CRATE_NAME") {
Some(c) => c,
None => "",
};
const krate: &[u8] = krate_str.as_bytes();
if krate.len() > 6
&& krate[0] == b'r'
&& krate[1] == b'u'
&& krate[2] == b's'
&& krate[3] == b't'
&& krate[4] == b'c'
&& krate[5] == b'_'
2022-11-07 19:47:32 +01:00
{
2022-11-08 00:42:00 +01:00
let slug = stringify!(#slug).as_bytes();
let mut pos = 0;
loop {
let b = slug[pos];
if krate.len() == pos + 6 {
if b != b'_' {
panic!(concat!(
"slug \"",
stringify!(#slug),
"\" does not match the crate it is in"
));
}
break;
2022-11-07 19:47:32 +01:00
}
2022-11-08 00:42:00 +01:00
let a = krate[pos + 6];
if a != b {
panic!(concat!(
"slug \"",
stringify!(#slug),
"\" does not match the crate it is in"
));
}
pos += 1;
2022-11-07 19:47:32 +01:00
}
2022-11-08 00:42:00 +01:00
} else {
// Crate does not start with "rustc_"
2022-11-07 19:47:32 +01:00
}
};
}
}