summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_macros/src/diagnostics/diagnostic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_macros/src/diagnostics/diagnostic.rs')
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs225
1 files changed, 225 insertions, 0 deletions
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
new file mode 100644
index 000000000..6b5b8b593
--- /dev/null
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -0,0 +1,225 @@
+#![deny(unused_must_use)]
+
+use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveBuilder, DiagnosticDeriveKind};
+use crate::diagnostics::error::{span_err, DiagnosticDeriveError};
+use crate::diagnostics::utils::{build_field_mapping, 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.
+pub(crate) struct SessionDiagnosticDerive<'a> {
+ structure: Structure<'a>,
+ sess: syn::Ident,
+ builder: DiagnosticDeriveBuilder,
+}
+
+impl<'a> SessionDiagnosticDerive<'a> {
+ pub(crate) fn new(diag: syn::Ident, sess: syn::Ident, structure: Structure<'a>) -> Self {
+ Self {
+ builder: DiagnosticDeriveBuilder {
+ diag,
+ fields: build_field_mapping(&structure),
+ kind: None,
+ code: None,
+ slug: None,
+ },
+ sess,
+ structure,
+ }
+ }
+
+ pub(crate) fn into_tokens(self) -> TokenStream {
+ let SessionDiagnosticDerive { mut structure, sess, mut builder } = self;
+
+ let ast = structure.ast();
+ let (implementation, param_ty) = {
+ 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.kind.value(), builder.slug.value()) {
+ (None, _) => {
+ span_err(span, "diagnostic kind not specified")
+ .help("use the `#[error(...)]` attribute to create an error")
+ .emit();
+ return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+ }
+ (Some(kind), None) => {
+ span_err(span, "diagnostic slug not specified")
+ .help(&format!(
+ "specify the slug as the first argument to the attribute, such as \
+ `#[{}(typeck::example_error)]`",
+ kind.descr()
+ ))
+ .emit();
+ return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+ }
+ (Some(DiagnosticDeriveKind::Lint), _) => {
+ span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported")
+ .help("use the `#[error(...)]` attribute to create a error")
+ .emit();
+ return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+ }
+ (Some(DiagnosticDeriveKind::Error), Some(slug)) => {
+ quote! {
+ let mut #diag = #sess.struct_err(rustc_errors::fluent::#slug);
+ }
+ }
+ (Some(DiagnosticDeriveKind::Warn), Some(slug)) => {
+ quote! {
+ let mut #diag = #sess.struct_warn(rustc_errors::fluent::#slug);
+ }
+ }
+ };
+
+ let implementation = quote! {
+ #init
+ #preamble
+ match self {
+ #attrs
+ }
+ match self {
+ #args
+ }
+ #diag
+ };
+ let param_ty = match builder.kind {
+ Some((DiagnosticDeriveKind::Error, _)) => {
+ quote! { rustc_errors::ErrorGuaranteed }
+ }
+ Some((DiagnosticDeriveKind::Lint | DiagnosticDeriveKind::Warn, _)) => {
+ quote! { () }
+ }
+ _ => unreachable!(),
+ };
+
+ (implementation, param_ty)
+ } else {
+ span_err(
+ ast.span().unwrap(),
+ "`#[derive(SessionDiagnostic)]` can only be used on structs",
+ )
+ .emit();
+
+ let implementation = DiagnosticDeriveError::ErrorHandled.to_compile_error();
+ let param_ty = quote! { rustc_errors::ErrorGuaranteed };
+ (implementation, param_ty)
+ }
+ };
+
+ structure.gen_impl(quote! {
+ gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty>
+ for @Self
+ {
+ fn into_diagnostic(
+ self,
+ #sess: &'__session_diagnostic_sess rustc_session::parse::ParseSess
+ ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, #param_ty> {
+ 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,
+ fields: build_field_mapping(&structure),
+ kind: None,
+ code: None,
+ slug: None,
+ },
+ 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.kind.value(), builder.slug.value()) {
+ (None, _) => {
+ span_err(span, "diagnostic kind not specified")
+ .help("use the `#[error(...)]` attribute to create an error")
+ .emit();
+ return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+ }
+ (Some(kind), None) => {
+ span_err(span, "diagnostic slug not specified")
+ .help(&format!(
+ "specify the slug as the first argument to the attribute, such as \
+ `#[{}(typeck::example_error)]`",
+ kind.descr()
+ ))
+ .emit();
+ return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+ }
+ (Some(DiagnosticDeriveKind::Error | DiagnosticDeriveKind::Warn), _) => {
+ span_err(span, "only `#[lint(..)]` is supported")
+ .help("use the `#[lint(...)]` attribute to create a lint")
+ .emit();
+ return DiagnosticDeriveError::ErrorHandled.to_compile_error();
+ }
+ (Some(DiagnosticDeriveKind::Lint), 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()
+ }
+ };
+
+ let diag = &builder.diag;
+ structure.gen_impl(quote! {
+ gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self {
+ fn decorate_lint(self, #diag: rustc_errors::LintDiagnosticBuilder<'__a, ()>) {
+ use rustc_errors::IntoDiagnosticArg;
+ #implementation
+ }
+ }
+ })
+ }
+}