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