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 4extern crate proc_macro; 5 6use crate::parse::parse_input; 7use crate::parse::Attribute; 8use proc_macro::TokenStream; 9use proc_macro2::{Literal, Span, TokenStream as TokenStream2, TokenTree}; 10use quote::{quote, quote_spanned}; 11 12use crate::settings::{Setting::*, *}; 13 14mod parse; 15mod settings; 16 17type Result<T> = std::result::Result<T, Error>; 18 19struct Error { 20 span: Span, 21 message: String, 22} 23 24impl 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::string(&self.message); 31 message.set_span(self.span); 32 quote_spanned!(self.span=> compile_error!{#message}) 33 } 34} 35 36#[proc_macro_attribute] 37pub 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 49fn 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))] 87fn 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)] 103fn 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 109fn detect_proc_macro_hack(attrs: &[Attribute]) -> bool { 110 attrs 111 .iter() 112 .any(|attr| attr.path_is_ident("proc_macro_hack")) 113} 114 115fn 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