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::DiagnosticDeriveKind;
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 kind = DiagnosticDeriveKind::Diagnostic;
23        let implementation = kind.each_variant(&mut structure, |mut builder, variant| {
24            let preamble = builder.preamble(variant);
25            let body = builder.body(variant);
26
27            let Some(message) = builder.primary_message() else {
28                return DiagnosticDeriveError::ErrorHandled.to_compile_error();
29            };
30            let message = message.diag_message(Some(variant));
31
32            let init = quote! {
33                let mut diag = rustc_errors::Diag::new(
34                    dcx,
35                    level,
36                    #message
37                );
38            };
39
40            let formatting_init = &builder.formatting_init;
41            quote! {
42                #init
43                #formatting_init
44                #preamble
45                #body
46                diag
47            }
48        });
49
50        // A lifetime of `'a` causes conflicts, but `_sess` is fine.
51        structure.gen_impl(quote! {
52            gen impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for @Self
53                where G: rustc_errors::EmissionGuarantee
54            {
55                #[track_caller]
56                fn into_diag(
57                    self,
58                    dcx: rustc_errors::DiagCtxtHandle<'_sess>,
59                    level: rustc_errors::Level
60                ) -> rustc_errors::Diag<'_sess, G> {
61                    #implementation
62                }
63            }
64        })
65    }
66}
67
68/// The central struct for constructing the `decorate_lint` method from an annotated struct.
69pub(crate) struct LintDiagnosticDerive<'a> {
70    structure: Structure<'a>,
71}
72
73impl<'a> LintDiagnosticDerive<'a> {
74    pub(crate) fn new(structure: Structure<'a>) -> Self {
75        Self { structure }
76    }
77
78    pub(crate) fn into_tokens(self) -> TokenStream {
79        let LintDiagnosticDerive { mut structure } = self;
80        let kind = DiagnosticDeriveKind::LintDiagnostic;
81        let implementation = kind.each_variant(&mut structure, |mut builder, variant| {
82            let preamble = builder.preamble(variant);
83            let body = builder.body(variant);
84
85            let Some(message) = builder.primary_message() else {
86                return DiagnosticDeriveError::ErrorHandled.to_compile_error();
87            };
88            let message = message.diag_message(Some(variant));
89            let primary_message = quote! {
90                diag.primary_message(#message);
91            };
92
93            let formatting_init = &builder.formatting_init;
94            quote! {
95                #primary_message
96                #preamble
97                #formatting_init
98                #body
99                diag
100            }
101        });
102
103        structure.gen_impl(quote! {
104            gen impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for @Self {
105                #[track_caller]
106                fn decorate_lint<'__b>(
107                    self,
108                    diag: &'__b mut rustc_errors::Diag<'__a, ()>
109                ) {
110                    #implementation;
111                }
112            }
113        })
114    }
115}