• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
new(span: Span, message: String) -> Self25     fn new(span: Span, message: String) -> Self {
26         Error { span, message }
27     }
28 
into_compile_error(self) -> TokenStream229     fn into_compile_error(self) -> TokenStream2 {
30         let mut message = 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]
proc_macro_error(attr: TokenStream, input: TokenStream) -> TokenStream37 pub fn proc_macro_error(attr: TokenStream, input: TokenStream) -> TokenStream {
38     match impl_proc_macro_error(attr.into(), input.clone().into()) {
39         Ok(ts) => ts,
40         Err(e) => {
41             let error = e.into_compile_error();
42             let input = TokenStream2::from(input);
43 
44             quote!(#input #error).into()
45         }
46     }
47 }
48 
impl_proc_macro_error(attr: TokenStream2, input: TokenStream2) -> Result<TokenStream>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))]
gen_body(block: TokenTree, settings: Settings) -> proc_macro2::TokenStream87 fn gen_body(block: TokenTree, settings: Settings) -> proc_macro2::TokenStream {
88     let is_proc_macro_hack = settings.is_set(ProcMacroHack);
89     let closure = if settings.is_set(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)]
gen_body(block: TokenTree, settings: Settings) -> proc_macro2::TokenStream103 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 
detect_proc_macro_hack(attrs: &[Attribute]) -> bool109 fn detect_proc_macro_hack(attrs: &[Attribute]) -> bool {
110     attrs
111         .iter()
112         .any(|attr| attr.path_is_ident("proc_macro_hack"))
113 }
114 
is_proc_macro(attrs: &[Attribute]) -> bool115 fn is_proc_macro(attrs: &[Attribute]) -> bool {
116     attrs.iter().any(|attr| {
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