]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_macros/src/diagnostics/diagnostic.rs
Refactor rustc lint API
[rust.git] / compiler / rustc_macros / src / diagnostics / diagnostic.rs
1 #![deny(unused_must_use)]
2
3 use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveBuilder, DiagnosticDeriveKind};
4 use crate::diagnostics::error::{span_err, DiagnosticDeriveError};
5 use crate::diagnostics::utils::SetOnce;
6 use proc_macro2::TokenStream;
7 use quote::quote;
8 use synstructure::Structure;
9
10 /// The central struct for constructing the `into_diagnostic` method from an annotated struct.
11 pub(crate) struct DiagnosticDerive<'a> {
12     structure: Structure<'a>,
13     handler: syn::Ident,
14     builder: DiagnosticDeriveBuilder,
15 }
16
17 impl<'a> DiagnosticDerive<'a> {
18     pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
19         Self {
20             builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic },
21             handler,
22             structure,
23         }
24     }
25
26     pub(crate) fn into_tokens(self) -> TokenStream {
27         let DiagnosticDerive { mut structure, handler, mut builder } = self;
28
29         let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
30             let preamble = builder.preamble(&variant);
31             let body = builder.body(&variant);
32
33             let diag = &builder.parent.diag;
34             let init = match builder.slug.value_ref() {
35                 None => {
36                     span_err(builder.span, "diagnostic slug not specified")
37                         .help(&format!(
38                             "specify the slug as the first argument to the `#[diag(...)]` \
39                             attribute, such as `#[diag(typeck::example_error)]`",
40                         ))
41                         .emit();
42                     return DiagnosticDeriveError::ErrorHandled.to_compile_error();
43                 }
44                 Some(slug) => {
45                     quote! {
46                         let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug);
47                     }
48                 }
49             };
50
51             quote! {
52                 #init
53                 #preamble
54                 #body
55                 #diag
56             }
57         });
58
59         structure.gen_impl(quote! {
60             gen impl<'__diagnostic_handler_sess, G>
61                     rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
62                     for @Self
63                 where G: rustc_errors::EmissionGuarantee
64             {
65                 fn into_diagnostic(
66                     self,
67                     #handler: &'__diagnostic_handler_sess rustc_errors::Handler
68                 ) -> rustc_errors::DiagnosticBuilder<'__diagnostic_handler_sess, G> {
69                     use rustc_errors::IntoDiagnosticArg;
70                     #implementation
71                 }
72             }
73         })
74     }
75 }
76
77 /// The central struct for constructing the `decorate_lint` method from an annotated struct.
78 pub(crate) struct LintDiagnosticDerive<'a> {
79     structure: Structure<'a>,
80     builder: DiagnosticDeriveBuilder,
81 }
82
83 impl<'a> LintDiagnosticDerive<'a> {
84     pub(crate) fn new(diag: syn::Ident, structure: Structure<'a>) -> Self {
85         Self {
86             builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::LintDiagnostic },
87             structure,
88         }
89     }
90
91     pub(crate) fn into_tokens(self) -> TokenStream {
92         let LintDiagnosticDerive { mut structure, mut builder } = self;
93
94         let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
95             let preamble = builder.preamble(&variant);
96             let body = builder.body(&variant);
97
98             let diag = &builder.parent.diag;
99
100             quote! {
101                 #preamble
102                 #body
103                 #diag
104             }
105         });
106
107         let msg = builder.each_variant(&mut structure, |mut builder, variant| {
108             // HACK(wafflelapkin): initialize slug (???)
109             let _preamble = builder.preamble(&variant);
110
111             match builder.slug.value_ref() {
112                 None => {
113                     span_err(builder.span, "diagnostic slug not specified")
114                         .help(&format!(
115                             "specify the slug as the first argument to the attribute, such as \
116                             `#[diag(typeck::example_error)]`",
117                         ))
118                         .emit();
119                     return DiagnosticDeriveError::ErrorHandled.to_compile_error();
120                 }
121                 Some(slug) => quote! { rustc_errors::fluent::#slug.into() },
122             }
123         });
124
125         let diag = &builder.diag;
126         structure.gen_impl(quote! {
127             gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self {
128                 fn decorate_lint<'__b>(self, #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()>) -> &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> {
129                     use rustc_errors::IntoDiagnosticArg;
130                     #implementation
131                 }
132
133                 fn msg(&self) -> rustc_errors::DiagnosticMessage {
134                     #msg
135                 }
136             }
137         })
138     }
139 }