diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index ce643ae89..590289f94 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -3,9 +3,13 @@ extern crate proc_macro; use darling::FromMeta; -use proc_macro::{Span, TokenStream}; +use proc_macro::TokenStream; +use proc_macro2::Span; use quote::{format_ident, quote}; +use std::iter; use syn::spanned::Spanned; +use syn::{parse, Type, Visibility}; +use syn::{ItemFn, ReturnType}; mod path; @@ -58,10 +62,9 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { fail = true; } if pool_size < 1 { - Span::call_site() - .error("pool_size must be 1 or greater") - .emit(); - fail = true + return parse::Error::new(Span::call_site(), "pool_size must be 1 or greater") + .to_compile_error() + .into(); } let mut arg_names: syn::punctuated::Punctuated = @@ -120,6 +123,66 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { result.into() } +#[proc_macro_attribute] +pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { + let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + + let fspan = f.span(); + let ident = f.sig.ident.clone(); + let ident_s = ident.to_string(); + + // XXX should we blacklist other attributes? + + let valid_signature = f.sig.constness.is_none() + && f.vis == 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 { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + Type::Never(..) => true, + _ => false, + }, + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", + ) + .to_compile_error() + .into(); + } + + f.block.stmts = iter::once( + syn::parse2(quote! {{ + // Check that this interrupt actually exists + let __irq_exists_check: interrupt::#ident; + }}) + .unwrap(), + ) + .chain(f.block.stmts) + .collect(); + + quote!( + #[doc(hidden)] + #[export_name = #ident_s] + #[allow(non_snake_case)] + #f + ) + .into() +} + #[proc_macro] pub fn interrupt_declare(item: TokenStream) -> TokenStream { let name = syn::parse_macro_input!(item as syn::Ident); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 2b35fdd6a..4349bf39f 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -72,3 +72,4 @@ pub mod interrupt { pub use embassy::interrupt::{declare, take, Interrupt}; pub use embassy_extras::interrupt::Priority3 as Priority; } +pub use embassy_macros::interrupt;