1 | //! This is `#[proc_macro_error]` attribute to be used with |
2 | //! [`proc-macro-error`](https://docs.rs/proc-macro-error/). There you go. |
3 | |
4 | extern crate proc_macro; |
5 | |
6 | use crate::parse::parse_input; |
7 | use crate::parse::Attribute; |
8 | use proc_macro::TokenStream; |
9 | use proc_macro2::{Literal, Span, TokenStream as TokenStream2, TokenTree}; |
10 | use quote::{quote, quote_spanned}; |
11 | |
12 | use crate::settings::{Setting::*, *}; |
13 | |
14 | mod parse; |
15 | mod settings; |
16 | |
17 | type Result<T> = std::result::Result<T, Error>; |
18 | |
19 | struct Error { |
20 | span: Span, |
21 | message: String, |
22 | } |
23 | |
24 | impl Error { |
25 | fn new(span: Span, message: String) -> Self { |
26 | Error { span, message } |
27 | } |
28 | |
29 | fn into_compile_error(self) -> TokenStream2 { |
30 | let mut message: Literal = Literal::string(&self.message); |
31 | message.set_span(self.span); |
32 | quote_spanned!(self.span=> compile_error!{#message}) |
33 | } |
34 | } |
35 | |
36 | #[proc_macro_attribute ] |
37 | pub fn proc_macro_error (attr: TokenStream, input: TokenStream) -> TokenStream { |
38 | match impl_proc_macro_error(attr:attr.into(), input:input.clone().into()) { |
39 | Ok(ts: TokenStream) => ts, |
40 | Err(e: Error) => { |
41 | let error: TokenStream = e.into_compile_error(); |
42 | let input: TokenStream = TokenStream2::from(input); |
43 | |
44 | quote!(#input #error).into() |
45 | } |
46 | } |
47 | } |
48 | |
49 | fn impl_proc_macro_error(attr: TokenStream2, input: TokenStream2) -> Result<TokenStream> { |
50 | let (attrs, signature, body) = parse_input(input)?; |
51 | let mut settings = parse_settings(attr)?; |
52 | |
53 | let is_proc_macro = is_proc_macro(&attrs); |
54 | if is_proc_macro { |
55 | settings.set(AssertUnwindSafe); |
56 | } |
57 | |
58 | if detect_proc_macro_hack(&attrs) { |
59 | settings.set(ProcMacroHack); |
60 | } |
61 | |
62 | if settings.is_set(ProcMacroHack) { |
63 | settings.set(AllowNotMacro); |
64 | } |
65 | |
66 | if !(settings.is_set(AllowNotMacro) || is_proc_macro) { |
67 | return Err(Error::new( |
68 | Span::call_site(), |
69 | "#[proc_macro_error] attribute can be used only with procedural macros \n\n \ |
70 | = hint: if you are really sure that #[proc_macro_error] should be applied \ |
71 | to this exact function, use #[proc_macro_error(allow_not_macro)] \n" |
72 | .into(), |
73 | )); |
74 | } |
75 | |
76 | let body = gen_body(body, settings); |
77 | |
78 | let res = quote! { |
79 | #(#attrs)* |
80 | #(#signature)* |
81 | { #body } |
82 | }; |
83 | Ok(res.into()) |
84 | } |
85 | |
86 | #[cfg (not(always_assert_unwind))] |
87 | fn gen_body(block: TokenTree, settings: Settings) -> proc_macro2::TokenStream { |
88 | let is_proc_macro_hack: bool = settings.is_set(setting:ProcMacroHack); |
89 | let closure: TokenStream = if settings.is_set(setting:AssertUnwindSafe) { |
90 | quote!(::std::panic::AssertUnwindSafe(|| #block )) |
91 | } else { |
92 | quote!(|| #block) |
93 | }; |
94 | |
95 | quote!( ::proc_macro_error::entry_point(#closure, #is_proc_macro_hack) ) |
96 | } |
97 | |
98 | // FIXME: |
99 | // proc_macro::TokenStream does not implement UnwindSafe until 1.37.0. |
100 | // Considering this is the closure's return type the unwind safety check would fail |
101 | // for virtually every closure possible, the check is meaningless. |
102 | #[cfg (always_assert_unwind)] |
103 | fn gen_body(block: TokenTree, settings: Settings) -> proc_macro2::TokenStream { |
104 | let is_proc_macro_hack = settings.is_set(ProcMacroHack); |
105 | let closure = quote!(::std::panic::AssertUnwindSafe(|| #block )); |
106 | quote!( ::proc_macro_error::entry_point(#closure, #is_proc_macro_hack) ) |
107 | } |
108 | |
109 | fn detect_proc_macro_hack(attrs: &[Attribute]) -> bool { |
110 | attrsIter<'_, Attribute> |
111 | .iter() |
112 | .any(|attr: &Attribute| attr.path_is_ident("proc_macro_hack" )) |
113 | } |
114 | |
115 | fn is_proc_macro(attrs: &[Attribute]) -> bool { |
116 | attrs.iter().any(|attr: &Attribute| { |
117 | attr.path_is_ident("proc_macro" ) |
118 | || attr.path_is_ident("proc_macro_derive" ) |
119 | || attr.path_is_ident("proc_macro_attribute" ) |
120 | }) |
121 | } |
122 | |