Skip to main content

rustc_macros/diagnostics/
diagnostic.rs

1#![deny(unused_must_use)]
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use synstructure::Structure;
6
7use crate::diagnostics::diagnostic_builder::each_variant;
8use crate::diagnostics::error::DiagnosticDeriveError;
9
10/// The central struct for constructing the `into_diag` method from an annotated struct.
11pub(crate) struct DiagnosticDerive<'a> {
12    structure: Structure<'a>,
13}
14
15impl<'a> DiagnosticDerive<'a> {
16    pub(crate) fn new(structure: Structure<'a>) -> Self {
17        Self { structure }
18    }
19
20    pub(crate) fn into_tokens(self) -> TokenStream {
21        let DiagnosticDerive { mut structure } = self;
22        let implementation = each_variant(&mut structure, |mut builder, variant| {
23            let preamble = builder.preamble(variant);
24            let body = builder.body(variant);
25
26            let Some(message) = builder.primary_message() else {
27                return DiagnosticDeriveError::ErrorHandled.to_compile_error();
28            };
29            let message = message.diag_message(Some(variant));
30
31            let init = quote! {
32                let mut diag = rustc_errors::Diag::new(
33                    dcx,
34                    level,
35                    #message
36                );
37            };
38
39            let formatting_init = &builder.formatting_init;
40            quote! {
41                #init
42                #formatting_init
43                #preamble
44                #body
45                diag
46            }
47        });
48
49        // A lifetime of `'a` causes conflicts, but `_sess` is fine.
50        structure.gen_impl(quote! {
51            gen impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for @Self
52                where G: rustc_errors::EmissionGuarantee
53            {
54                #[track_caller]
55                fn into_diag(
56                    self,
57                    dcx: rustc_errors::DiagCtxtHandle<'_sess>,
58                    level: rustc_errors::Level
59                ) -> rustc_errors::Diag<'_sess, G> {
60                    #implementation
61                }
62            }
63        })
64    }
65}