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