1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
extern crate proc_macro; use ::proc_macro::TokenStream; use ::syn::{self, ItemFn, parse_macro_input, spanned::Spanned, }; use ::quote::{ quote, quote_spanned, }; #[cfg(not(feature = "test"))] macro_rules! CRATE_NAME {() => ( ::core::convert::identity::<syn::Ident>(syn::parse_str( &::proc_macro_crate::crate_name("function_name") .expect("Cargo.toml must have a function_name dependency") ).unwrap()) )} #[cfg(feature = "test")] macro_rules! CRATE_NAME {() => ( ::core::convert::identity::<syn::Ident>(syn::parse_quote! { function_name }) )} const IDENT_SUFFIX: &'static str = "__hack__"; #[proc_macro_attribute] pub fn named (params: TokenStream, input: TokenStream) -> TokenStream { if params.clone().into_iter().next().is_some() { return syn::Error::new_spanned( if true { params.into() } else { quote!() }, "#[named] does not take arguments", ).to_compile_error().into(); } let mut input_fn = parse_macro_input!(input as ItemFn); let ident = syn::Ident::new( &format!("{}{}", input_fn.ident, IDENT_SUFFIX), input_fn.ident.span(), ); let _crate = CRATE_NAME!(); let block = *input_fn.block; *input_fn.block = syn::parse_quote! { { #[allow(dead_code)] #[allow(non_camel_case_types)] #[derive(::#_crate::named_hack)] enum #ident {} #block } }; TokenStream::from(quote_spanned! { input_fn.span() => #input_fn }) } #[doc(hidden)] #[proc_macro_derive(named_hack)] pub fn hack (input: TokenStream) -> TokenStream { let input: syn::DeriveInput = parse_macro_input!(input); let ident = input.ident.to_string(); let ident = &ident[.. ident.len() - IDENT_SUFFIX.len()]; let fname = syn::LitStr::new(ident, input.ident.span()); TokenStream::from(quote! { macro_rules! function_name {() => ( #fname )} }) }