2022-04-27 02:57:44 +01:00
|
|
|
#![deny(unused_must_use)]
|
|
|
|
|
|
2022-06-30 08:57:45 +01:00
|
|
|
use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveBuilder, DiagnosticDeriveKind};
|
|
|
|
|
use crate::diagnostics::error::{span_err, DiagnosticDeriveError};
|
2022-09-23 12:49:02 +01:00
|
|
|
use crate::diagnostics::utils::SetOnce;
|
2022-06-30 08:57:45 +01:00
|
|
|
use proc_macro2::TokenStream;
|
|
|
|
|
use quote::quote;
|
2022-11-13 13:10:36 +01:00
|
|
|
use syn::spanned::Spanned;
|
2022-06-30 08:57:45 +01:00
|
|
|
use synstructure::Structure;
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2022-04-27 05:24:31 +01:00
|
|
|
/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
|
2022-09-15 00:01:44 -04:00
|
|
|
pub(crate) struct DiagnosticDerive<'a> {
|
2022-04-27 02:57:44 +01:00
|
|
|
structure: Structure<'a>,
|
2022-06-30 08:57:45 +01:00
|
|
|
builder: DiagnosticDeriveBuilder,
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-15 00:01:44 -04:00
|
|
|
impl<'a> DiagnosticDerive<'a> {
|
2022-09-18 11:45:41 -04:00
|
|
|
pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
|
2022-04-27 02:57:44 +01:00
|
|
|
Self {
|
2022-10-03 14:24:17 +01:00
|
|
|
builder: DiagnosticDeriveBuilder {
|
|
|
|
|
diag,
|
|
|
|
|
kind: DiagnosticDeriveKind::Diagnostic { handler },
|
|
|
|
|
},
|
2022-04-27 02:57:44 +01:00
|
|
|
structure,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn into_tokens(self) -> TokenStream {
|
2022-10-03 14:24:17 +01:00
|
|
|
let DiagnosticDerive { mut structure, mut builder } = self;
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2022-09-23 12:49:02 +01:00
|
|
|
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
|
2022-11-29 11:01:17 +00:00
|
|
|
let preamble = builder.preamble(variant);
|
|
|
|
|
let body = builder.body(variant);
|
2022-09-23 12:49:02 +01:00
|
|
|
|
|
|
|
|
let diag = &builder.parent.diag;
|
2022-10-03 14:24:17 +01:00
|
|
|
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
|
|
|
|
|
unreachable!()
|
|
|
|
|
};
|
2022-09-23 12:49:02 +01:00
|
|
|
let init = match builder.slug.value_ref() {
|
|
|
|
|
None => {
|
|
|
|
|
span_err(builder.span, "diagnostic slug not specified")
|
2022-11-29 11:01:17 +00:00
|
|
|
.help(format!(
|
2022-09-23 12:49:02 +01:00
|
|
|
"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)]`",
|
2022-09-23 12:49:02 +01:00
|
|
|
))
|
|
|
|
|
.emit();
|
|
|
|
|
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
|
|
|
|
}
|
2022-11-13 13:10:36 +01:00
|
|
|
Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => {
|
|
|
|
|
span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match")
|
2022-11-29 11:01:17 +00:00
|
|
|
.note(format!(
|
2022-11-13 13:10:36 +01:00
|
|
|
"slug is `{slug_name}` but the crate name is `{crate_name}`"
|
|
|
|
|
))
|
2022-11-29 11:01:17 +00:00
|
|
|
.help(format!(
|
2022-11-13 13:10:36 +01:00
|
|
|
"expected a slug starting with `{slug_prefix}_...`"
|
|
|
|
|
))
|
|
|
|
|
.emit();
|
|
|
|
|
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
|
|
|
|
}
|
2022-09-23 12:49:02 +01:00
|
|
|
Some(slug) => {
|
|
|
|
|
quote! {
|
|
|
|
|
let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug);
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
2022-08-19 15:02:10 +02:00
|
|
|
}
|
2022-09-23 12:49:02 +01:00
|
|
|
};
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2022-10-03 16:10:34 +01:00
|
|
|
let formatting_init = &builder.formatting_init;
|
2022-09-23 12:49:02 +01:00
|
|
|
quote! {
|
|
|
|
|
#init
|
2022-10-03 16:10:34 +01:00
|
|
|
#formatting_init
|
2022-09-23 12:49:02 +01:00
|
|
|
#preamble
|
|
|
|
|
#body
|
|
|
|
|
#diag
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
2022-09-23 12:49:02 +01:00
|
|
|
});
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2022-10-03 14:24:17 +01:00
|
|
|
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() };
|
2022-04-27 02:57:44 +01:00
|
|
|
structure.gen_impl(quote! {
|
2022-09-18 11:45:41 -04:00
|
|
|
gen impl<'__diagnostic_handler_sess, G>
|
|
|
|
|
rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
|
2022-04-27 02:57:44 +01:00
|
|
|
for @Self
|
2022-08-19 15:02:10 +02:00
|
|
|
where G: rustc_errors::EmissionGuarantee
|
2022-04-27 02:57:44 +01:00
|
|
|
{
|
2022-10-31 16:14:29 +01:00
|
|
|
|
|
|
|
|
#[track_caller]
|
2022-04-27 02:57:44 +01:00
|
|
|
fn into_diagnostic(
|
|
|
|
|
self,
|
2022-09-18 11:45:41 -04:00
|
|
|
#handler: &'__diagnostic_handler_sess rustc_errors::Handler
|
|
|
|
|
) -> rustc_errors::DiagnosticBuilder<'__diagnostic_handler_sess, G> {
|
2022-04-27 02:57:44 +01:00
|
|
|
use rustc_errors::IntoDiagnosticArg;
|
|
|
|
|
#implementation
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-30 08:57:45 +01:00
|
|
|
/// The central struct for constructing the `decorate_lint` method from an annotated struct.
|
|
|
|
|
pub(crate) struct LintDiagnosticDerive<'a> {
|
|
|
|
|
structure: Structure<'a>,
|
|
|
|
|
builder: DiagnosticDeriveBuilder,
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
|
2022-06-30 08:57:45 +01:00
|
|
|
impl<'a> LintDiagnosticDerive<'a> {
|
|
|
|
|
pub(crate) fn new(diag: syn::Ident, structure: Structure<'a>) -> Self {
|
|
|
|
|
Self {
|
2022-09-23 12:49:02 +01:00
|
|
|
builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::LintDiagnostic },
|
2022-06-30 08:57:45 +01:00
|
|
|
structure,
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-30 08:57:45 +01:00
|
|
|
pub(crate) fn into_tokens(self) -> TokenStream {
|
|
|
|
|
let LintDiagnosticDerive { mut structure, mut builder } = self;
|
2022-06-23 14:51:44 +01:00
|
|
|
|
2022-09-23 12:49:02 +01:00
|
|
|
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
|
2022-11-29 11:01:17 +00:00
|
|
|
let preamble = builder.preamble(variant);
|
|
|
|
|
let body = builder.body(variant);
|
2022-09-23 12:49:02 +01:00
|
|
|
|
|
|
|
|
let diag = &builder.parent.diag;
|
2022-10-03 16:10:34 +01:00
|
|
|
let formatting_init = &builder.formatting_init;
|
2022-09-16 11:01:02 +04:00
|
|
|
quote! {
|
|
|
|
|
#preamble
|
2022-10-03 16:10:34 +01:00
|
|
|
#formatting_init
|
2022-09-16 11:01:02 +04:00
|
|
|
#body
|
|
|
|
|
#diag
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let msg = builder.each_variant(&mut structure, |mut builder, variant| {
|
2022-10-03 15:34:00 +01:00
|
|
|
// Collect the slug by generating the preamble.
|
2022-11-29 11:01:17 +00:00
|
|
|
let _ = builder.preamble(variant);
|
2022-09-16 11:01:02 +04:00
|
|
|
|
|
|
|
|
match builder.slug.value_ref() {
|
2022-09-23 12:49:02 +01:00
|
|
|
None => {
|
|
|
|
|
span_err(builder.span, "diagnostic slug not specified")
|
2022-11-29 11:01:17 +00:00
|
|
|
.help(format!(
|
2022-09-23 12:49:02 +01:00
|
|
|
"specify the slug as the first argument to the attribute, such as \
|
2022-10-22 11:07:54 +02:00
|
|
|
`#[diag(compiletest_example)]`",
|
2022-09-23 12:49:02 +01:00
|
|
|
))
|
|
|
|
|
.emit();
|
2022-11-29 11:01:17 +00:00
|
|
|
DiagnosticDeriveError::ErrorHandled.to_compile_error()
|
2022-09-23 12:49:02 +01:00
|
|
|
}
|
2022-11-13 13:10:36 +01:00
|
|
|
Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => {
|
|
|
|
|
span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match")
|
2022-11-29 11:01:17 +00:00
|
|
|
.note(format!(
|
2022-11-13 13:10:36 +01:00
|
|
|
"slug is `{slug_name}` but the crate name is `{crate_name}`"
|
|
|
|
|
))
|
2022-11-29 11:01:17 +00:00
|
|
|
.help(format!(
|
2022-11-13 13:10:36 +01:00
|
|
|
"expected a slug starting with `{slug_prefix}_...`"
|
|
|
|
|
))
|
|
|
|
|
.emit();
|
2022-11-29 11:01:17 +00:00
|
|
|
DiagnosticDeriveError::ErrorHandled.to_compile_error()
|
2022-11-13 13:10:36 +01:00
|
|
|
}
|
2022-11-07 19:47:32 +01:00
|
|
|
Some(slug) => {
|
|
|
|
|
quote! {
|
|
|
|
|
rustc_errors::fluent::#slug.into()
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
2022-09-23 12:49:02 +01:00
|
|
|
});
|
2022-04-27 02:57:44 +01:00
|
|
|
|
2022-06-30 08:57:45 +01:00
|
|
|
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]
|
2022-10-03 15:34:00 +01:00
|
|
|
fn decorate_lint<'__b>(
|
|
|
|
|
self,
|
|
|
|
|
#diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>
|
|
|
|
|
) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> {
|
2022-06-30 08:57:45 +01:00
|
|
|
use rustc_errors::IntoDiagnosticArg;
|
|
|
|
|
#implementation
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
2022-09-16 11:01:02 +04:00
|
|
|
|
|
|
|
|
fn msg(&self) -> rustc_errors::DiagnosticMessage {
|
|
|
|
|
#msg
|
|
|
|
|
}
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
2022-06-30 08:57:45 +01:00
|
|
|
})
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-07 19:47:32 +01:00
|
|
|
|
2022-11-13 13:10:36 +01:00
|
|
|
struct Mismatch {
|
|
|
|
|
slug_name: String,
|
|
|
|
|
crate_name: String,
|
|
|
|
|
slug_prefix: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Mismatch {
|
|
|
|
|
/// Checks whether the slug starts with the crate name it's in.
|
|
|
|
|
fn check(slug: &syn::Path) -> Option<Mismatch> {
|
|
|
|
|
// If this is missing we're probably in a test, so bail.
|
|
|
|
|
let crate_name = std::env::var("CARGO_CRATE_NAME").ok()?;
|
|
|
|
|
|
|
|
|
|
// If we're not in a "rustc_" crate, bail.
|
2022-12-15 00:06:34 +01:00
|
|
|
let Some(("rustc", slug_prefix)) = crate_name.split_once('_') else { return None };
|
2022-11-13 13:10:36 +01:00
|
|
|
|
|
|
|
|
let slug_name = slug.segments.first()?.ident.to_string();
|
|
|
|
|
if !slug_name.starts_with(slug_prefix) {
|
|
|
|
|
Some(Mismatch { slug_name, slug_prefix: slug_prefix.to_string(), crate_name })
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
2022-11-07 19:47:32 +01:00
|
|
|
}
|
|
|
|
|
}
|