@@ -18,6 +18,9 @@ use syn::{
1818
1919/// Creates an `unsafe` program entry point (i.e. a `kmain` function).
2020///
21+ /// It's `unsafe` because you are not supposed to call it - it should only be
22+ /// called from the start-up code once initialisation is complete.
23+ ///
2124/// When placed on a function like:
2225///
2326/// ```rust ignore
@@ -81,7 +84,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
8184 let tramp_ident = Ident :: new ( "__cortex_ar_rt_kmain" , Span :: call_site ( ) ) ;
8285 let ident = & f. sig . ident ;
8386
84- if let Err ( error) = check_attr_whitelist ( & f. attrs , WhiteListCaller :: Entry ) {
87+ if let Err ( error) = check_attr_whitelist ( & f. attrs , Kind :: Entry ) {
8588 return error;
8689 }
8790
@@ -125,6 +128,9 @@ impl std::fmt::Display for Exception {
125128
126129/// Creates an `unsafe` exception handler.
127130///
131+ /// It's `unsafe` because you are not supposed to call it - it should only be
132+ /// called from assembly routines registered in the interrupt vector table.
133+ ///
128134/// When placed on a function like:
129135///
130136/// ```rust ignore
@@ -153,34 +159,95 @@ impl std::fmt::Display for Exception {
153159/// * SvcHandler (creates `_svc_handler`)
154160/// * PrefetchHandler (creates `_prefetch_handler`)
155161/// * AbortHandler (creates `_abort_handler`)
156- /// * IrqHandler (creates `_irq_handler`)
162+ /// * IrqHandler (creates `_irq_handler`) - you can also use `#[interrupt]` if you prefer.
157163#[ proc_macro_attribute]
158164pub fn exception ( args : TokenStream , input : TokenStream ) -> TokenStream {
165+ handle_exception_interrupt ( args, input, Kind :: Exception )
166+ }
167+
168+ /// Creates an `unsafe` interrupt handler.
169+ ///
170+ /// It's `unsafe` because you are not supposed to call it - it should only be
171+ /// called from assembly routines registered in the interrupt vector table.
172+ ///
173+ /// When placed on a function like:
174+ ///
175+ /// ```rust ignore
176+ /// #[interrupt]
177+ /// fn foo(addr: usize) -> ! {
178+ /// panic!("On no")
179+ /// }
180+ /// ```
181+ ///
182+ /// You get something like:
183+ ///
184+ /// ```rust
185+ /// #[export_name = "_interrupt_handler"]
186+ /// pub unsafe extern "C" fn __cortex_ar_rt_interrupt_handler(addr: usize) -> ! {
187+ /// foo(addr)
188+ /// }
189+ ///
190+ /// fn foo(addr: usize) -> ! {
191+ /// panic!("On no")
192+ /// }
193+ /// ```
194+ ///
195+ /// This is just a convienient short-hand for `#[exception(IrqHandler)` because
196+ /// you may not consider interrupts to be a form of exception.
197+ #[ proc_macro_attribute]
198+ pub fn interrupt ( args : TokenStream , input : TokenStream ) -> TokenStream {
199+ handle_exception_interrupt ( args, input, Kind :: Interrupt )
200+ }
201+
202+ /// Note if we got `#[entry]`, `#[exception(...)]` or `#[interrupt]`
203+ #[ derive( PartialEq , Eq , Clone , Copy , Debug ) ]
204+ enum Kind {
205+ /// Corresponds to `#[entry]`
206+ Entry ,
207+ /// Corresponds to `#[exception(...)]`
208+ Exception ,
209+ /// Corresponds to `#[interrupt]`
210+ Interrupt ,
211+ }
212+
213+ /// A common routine for handling exception or interrupt functions
214+ fn handle_exception_interrupt (
215+ args : TokenStream ,
216+ input : TokenStream ,
217+ kind : Kind ,
218+ ) -> TokenStream {
159219 let f = parse_macro_input ! ( input as ItemFn ) ;
160220
161- if let Err ( error) = check_attr_whitelist ( & f. attrs , WhiteListCaller :: Exception ) {
221+ if let Err ( error) = check_attr_whitelist ( & f. attrs , kind ) {
162222 return error;
163223 }
164224
165- let mut args_iter = args. into_iter ( ) ;
166- let Some ( TokenTree :: Ident ( exception_name) ) = args_iter. next ( ) else {
167- return parse:: Error :: new (
168- Span :: call_site ( ) ,
169- "This attribute requires the name of the exception as the first argument" ,
170- )
171- . to_compile_error ( )
172- . into ( ) ;
225+ let exception_name = match kind {
226+ Kind :: Entry => {
227+ panic ! ( "Don't handle #[entry] with `handle_exception_interrupt`!" ) ;
228+ }
229+ Kind :: Exception => {
230+ let mut args_iter = args. into_iter ( ) ;
231+ let Some ( TokenTree :: Ident ( exception_name) ) = args_iter. next ( ) else {
232+ return parse:: Error :: new (
233+ Span :: call_site ( ) ,
234+ "This attribute requires the name of the exception as the first argument" ,
235+ )
236+ . to_compile_error ( )
237+ . into ( ) ;
238+ } ;
239+ if !args_iter. next ( ) . is_none ( ) {
240+ return parse:: Error :: new (
241+ Span :: call_site ( ) ,
242+ "This attribute accepts only one argument" ,
243+ )
244+ . to_compile_error ( )
245+ . into ( ) ;
246+ }
247+ exception_name. to_string ( )
248+ }
249+ Kind :: Interrupt => "IrqHandler" . to_string ( ) ,
173250 } ;
174- if !args_iter. next ( ) . is_none ( ) {
175- return parse:: Error :: new (
176- Span :: call_site ( ) ,
177- "This attribute accepts only one argument" ,
178- )
179- . to_compile_error ( )
180- . into ( ) ;
181- }
182-
183- let exception_name = exception_name. to_string ( ) ;
184251
185252 let exn = match exception_name. as_str ( ) {
186253 "UndefinedHandler" => Exception :: UndefinedHandler ,
@@ -328,6 +395,9 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
328395 . into ( )
329396}
330397
398+ /// Given a list of attributes, split them into `cfg` and non-`cfg`.
399+ ///
400+ /// Returns `(cfgs, non_cfgs)`.
331401fn extract_cfgs ( attrs : Vec < Attribute > ) -> ( Vec < Attribute > , Vec < Attribute > ) {
332402 let mut cfgs = vec ! [ ] ;
333403 let mut not_cfgs = vec ! [ ] ;
@@ -343,12 +413,8 @@ fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
343413 ( cfgs, not_cfgs)
344414}
345415
346- enum WhiteListCaller {
347- Entry ,
348- Exception ,
349- }
350-
351- fn check_attr_whitelist ( attrs : & [ Attribute ] , caller : WhiteListCaller ) -> Result < ( ) , TokenStream > {
416+ /// Check whether any disallowed attributes have been applied to our entry/exception function.
417+ fn check_attr_whitelist ( attrs : & [ Attribute ] , caller : Kind ) -> Result < ( ) , TokenStream > {
352418 let whitelist = & [
353419 "doc" ,
354420 "link_section" ,
@@ -370,12 +436,15 @@ fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<
370436 }
371437
372438 let err_str = match caller {
373- WhiteListCaller :: Entry => {
439+ Kind :: Entry => {
374440 "this attribute is not allowed on a cortex-r-rt/cortex-a-rt entry point"
375441 }
376- WhiteListCaller :: Exception => {
442+ Kind :: Exception => {
377443 "this attribute is not allowed on an exception handler controlled by cortex-r-rt/cortex-a-rt"
378444 }
445+ Kind :: Interrupt => {
446+ "this attribute is not allowed on an interrupt handler controlled by cortex-r-rt/cortex-a-rt"
447+ }
379448 } ;
380449
381450 return Err ( parse:: Error :: new ( attr. span ( ) , err_str)
0 commit comments