// Adapted from https://github.com/rust-embedded/cortex-m-rt/blob/master/macros/src/lib.rs extern crate proc_macro; mod vector; use syn::spanned::Spanned; #[proc_macro_attribute] pub fn entry( args: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let f = syn::parse_macro_input!(input as syn::ItemFn); // check the function signature let valid_signature = f.sig.constness.is_none() && f.vis == syn::Visibility::Inherited && f.sig.abi.is_none() && f.sig.inputs.is_empty() && f.sig.generics.params.is_empty() && f.sig.generics.where_clause.is_none() && f.sig.variadic.is_none() && match f.sig.output { syn::ReturnType::Default => false, syn::ReturnType::Type(_, ref ty) => match **ty { syn::Type::Never(_) => true, _ => false, }, }; if !valid_signature { return syn::parse::Error::new( f.span(), "`#[entry]` function must have signature `[unsafe] fn() -> !`", ) .to_compile_error() .into(); } if !args.is_empty() { return syn::parse::Error::new( proc_macro2::Span::call_site(), "This attribute accepts no arguments", ) .to_compile_error() .into(); } // Rename the function so it is not callable let ident = syn::Ident::new( &format!("_avr_device_rt_{}", f.sig.ident), proc_macro2::Span::call_site(), ); let attrs = f.attrs; let block = f.block; let stmts = block.stmts; let unsafety = f.sig.unsafety; quote::quote! ( #[cfg(not(any(doc, target_arch = "avr")))] compile_error!( "Ensure that you are using an AVR target! You may need to change \ directories or pass a --target flag to cargo. See https://github.com/Rahix/avr-device/pull/41 for more details." ); #(#attrs)* #[doc(hidden)] #[export_name = "main"] pub #unsafety extern "C" fn #ident() -> ! { #(#stmts)* } ) .into() } #[proc_macro_attribute] pub fn interrupt( args: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let f = syn::parse_macro_input!(input as syn::ItemFn); let args: Vec<_> = args.into_iter().collect(); let fspan = f.span(); let ident = f.sig.ident; let ident_s = ident.to_string(); let attrs = f.attrs; let block = f.block; let stmts = block.stmts; let unsafety = f.sig.unsafety; let chip = if let Some(tree) = args.get(0) { if let proc_macro::TokenTree::Ident(ident) = tree { ident.to_string() } else { return syn::parse::Error::new( proc_macro2::Span::call_site(), "#[interrupt(chip)]: chip must be an ident", ) .to_compile_error() .into(); } } else { return syn::parse::Error::new( proc_macro2::Span::call_site(), "#[interrupt(chip)] needs a chip argument", ) .to_compile_error() .into(); }; let valid_signature = f.sig.constness.is_none() && f.vis == syn::Visibility::Inherited && f.sig.abi.is_none() && f.sig.inputs.is_empty() && f.sig.generics.params.is_empty() && f.sig.generics.where_clause.is_none() && f.sig.variadic.is_none() && match f.sig.output { syn::ReturnType::Default => true, syn::ReturnType::Type(_, ref ty) => match **ty { syn::Type::Tuple(ref tuple) => tuple.elems.is_empty(), syn::Type::Never(..) => true, _ => false, }, }; if !valid_signature { return syn::parse::Error::new( fspan, "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", ) .to_compile_error() .into(); } let vect = if let Some(v) = vector::lookup_vector(&chip, &ident_s) { v } else { return syn::parse::Error::new( proc_macro2::Span::call_site(), &format!("Chip `{}` or interrupt `{}` unknown", chip, ident_s), ) .to_compile_error() .into(); }; let vector = format!("__vector_{}", vect); let vector_ident = syn::Ident::new(&vector, proc_macro2::Span::call_site()); quote::quote! ( #[no_mangle] pub unsafe extern "avr-interrupt" fn #vector_ident() { #ident(); } #(#attrs)* #[allow(non_snake_case)] #unsafety fn #ident() { #(#stmts)* } ) .into() }