diff options
author | 2018-10-26 21:22:43 +0200 | |
---|---|---|
committer | 2018-10-26 21:22:43 +0200 | |
commit | adf49361cd19291005bbc9645e41ed334abb2e66 (patch) | |
tree | 9ef9dc68d78025cafdf6f1a1b79e91d10333e3af /cortex-m-rt/macros/src/lib.rs | |
parent | c5d24f867d7d4f4f270ba99eef89f97cf38a4445 (diff) | |
download | cortex-m-adf49361cd19291005bbc9645e41ed334abb2e66.tar.gz cortex-m-adf49361cd19291005bbc9645e41ed334abb2e66.tar.zst cortex-m-adf49361cd19291005bbc9645e41ed334abb2e66.zip |
attributes: turn panics into compile errors
Sample error messages:
```
error: `#[entry]` function must have signature `[unsafe] fn() -> !`
--> examples/error.rs:15:1
|
15 | fn main() {
| ^^
error: aborting due to previous error
```
```
error: This attribute accepts no arguments
--> examples/error.rs:14:1
|
14 | #[entry(hello)]
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
```
```
error: This is not a valid exception name
--> examples/error.rs:20:4
|
20 | fn foo() {}
| ^^^
error: aborting due to previous error
```
Diffstat (limited to 'cortex-m-rt/macros/src/lib.rs')
-rw-r--r-- | cortex-m-rt/macros/src/lib.rs | 343 |
1 files changed, 201 insertions, 142 deletions
diff --git a/cortex-m-rt/macros/src/lib.rs b/cortex-m-rt/macros/src/lib.rs index dd89f06..54bbdfa 100644 --- a/cortex-m-rt/macros/src/lib.rs +++ b/cortex-m-rt/macros/src/lib.rs @@ -14,7 +14,10 @@ use rand::Rng; use rand::SeedableRng; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; -use syn::{FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, Visibility}; +use syn::{ + parse, spanned::Spanned, FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, + Visibility, +}; static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); @@ -81,28 +84,35 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // check the function signature - assert!( - f.constness.is_none() - && f.vis == Visibility::Inherited - && f.abi.is_none() - && f.decl.inputs.is_empty() - && f.decl.generics.params.is_empty() - && f.decl.generics.where_clause.is_none() - && f.decl.variadic.is_none() - && match f.decl.output { - ReturnType::Default => false, - ReturnType::Type(_, ref ty) => match **ty { - Type::Never(_) => true, - _ => false, - }, + let valid_signature = f.constness.is_none() + && f.vis == Visibility::Inherited + && f.abi.is_none() + && f.decl.inputs.is_empty() + && f.decl.generics.params.is_empty() + && f.decl.generics.where_clause.is_none() + && f.decl.variadic.is_none() + && match f.decl.output { + ReturnType::Default => false, + ReturnType::Type(_, ref ty) => match **ty { + Type::Never(_) => true, + _ => false, }, - "`#[entry]` function must have signature `[unsafe] fn() -> !`" - ); + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[entry]` function must have signature `[unsafe] fn() -> !`", + ) + .to_compile_error() + .into(); + } - assert!( - args.to_string() == "", - "`entry` attribute must have no arguments" - ); + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } // XXX should we blacklist other attributes? let attrs = f.attrs; @@ -127,7 +137,8 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { &mut #ident }; ) - }).collect::<Vec<_>>(); + }) + .collect::<Vec<_>>(); quote!( #[export_name = "main"] @@ -137,7 +148,8 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .into() } /// Attribute to declare an exception handler @@ -255,11 +267,13 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); - assert!( - args.to_string() == "", - "`exception` attribute must have no arguments" - ); + 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.ident; enum Exception { @@ -276,7 +290,11 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { // MemoryManagement is not available on Cortex-M0) "NonMaskableInt" | "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall" | "DebugMonitor" | "PendSV" | "SysTick" => Exception::Other, - _ => panic!("{} is not a valid exception name", ident_s), + _ => { + return parse::Error::new(ident.span(), "This is not a valid exception name") + .to_compile_error() + .into(); + } }; // XXX should we blacklist other attributes? @@ -288,24 +306,30 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { let hash = random_ident(); match exn { Exception::DefaultHandler => { - assert!( - f.constness.is_none() - && f.vis == Visibility::Inherited - && f.abi.is_none() - && f.decl.inputs.len() == 1 - && f.decl.generics.params.is_empty() - && f.decl.generics.where_clause.is_none() - && f.decl.variadic.is_none() - && match f.decl.output { - ReturnType::Default => true, - ReturnType::Type(_, ref ty) => match **ty { - Type::Tuple(ref tuple) => tuple.elems.is_empty(), - Type::Never(..) => true, - _ => false, - }, + let valid_signature = f.constness.is_none() + && f.vis == Visibility::Inherited + && f.abi.is_none() + && f.decl.inputs.len() == 1 + && f.decl.generics.params.is_empty() + && f.decl.generics.where_clause.is_none() + && f.decl.variadic.is_none() + && match f.decl.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + Type::Never(..) => true, + _ => false, }, - "`DefaultHandler` exception must have signature `[unsafe] fn(i16) [-> !]`" - ); + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`DefaultHandler` must have signature `[unsafe] fn(i16) [-> !]`", + ) + .to_compile_error() + .into(); + } let arg = match f.decl.inputs[0] { FnArg::Captured(ref arg) => arg, @@ -324,35 +348,40 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .into() } Exception::HardFault => { - assert!( - f.constness.is_none() - && f.vis == Visibility::Inherited - && f.abi.is_none() - && f.decl.inputs.len() == 1 - && match f.decl.inputs[0] { - FnArg::Captured(ref arg) => match arg.ty { - Type::Reference(ref r) => { - r.lifetime.is_none() && r.mutability.is_none() - } - _ => false, - }, + let valid_signature = f.constness.is_none() + && f.vis == Visibility::Inherited + && f.abi.is_none() + && f.decl.inputs.len() == 1 + && match f.decl.inputs[0] { + FnArg::Captured(ref arg) => match arg.ty { + Type::Reference(ref r) => r.lifetime.is_none() && r.mutability.is_none(), _ => false, - } - && f.decl.generics.params.is_empty() - && f.decl.generics.where_clause.is_none() - && f.decl.variadic.is_none() - && match f.decl.output { - ReturnType::Default => false, - ReturnType::Type(_, ref ty) => match **ty { - Type::Never(_) => true, - _ => false, - }, }, - "`HardFault` exception must have signature `[unsafe] fn(&ExceptionFrame) -> !`" - ); + _ => false, + } + && f.decl.generics.params.is_empty() + && f.decl.generics.where_clause.is_none() + && f.decl.variadic.is_none() + && match f.decl.output { + ReturnType::Default => false, + ReturnType::Type(_, ref ty) => match **ty { + Type::Never(_) => true, + _ => false, + }, + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`HardFault` handler must have signature `[unsafe] fn(&ExceptionFrame) -> !`", + ) + .to_compile_error() + .into(); + } let arg = match f.decl.inputs[0] { FnArg::Captured(ref arg) => arg, @@ -372,28 +401,35 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .into() } Exception::Other => { - assert!( - f.constness.is_none() - && f.vis == Visibility::Inherited - && f.abi.is_none() - && f.decl.inputs.is_empty() - && f.decl.generics.params.is_empty() - && f.decl.generics.where_clause.is_none() - && f.decl.variadic.is_none() - && match f.decl.output { - ReturnType::Default => true, - ReturnType::Type(_, ref ty) => match **ty { - Type::Tuple(ref tuple) => tuple.elems.is_empty(), - Type::Never(..) => true, - _ => false, - }, + let valid_signature = f.constness.is_none() + && f.vis == Visibility::Inherited + && f.abi.is_none() + && f.decl.inputs.is_empty() + && f.decl.generics.params.is_empty() + && f.decl.generics.where_clause.is_none() + && f.decl.variadic.is_none() + && match f.decl.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + Type::Never(..) => true, + _ => false, }, - "`#[exception]` functions other than `DefaultHandler` and `HardFault` must \ - have signature `[unsafe] fn() [-> !]`" - ); + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`#[exception]` handlers other than `DefaultHandler` and `HardFault` must have \ + signature `[unsafe] fn() [-> !]`", + ) + .to_compile_error() + .into(); + } let (statics, stmts) = extract_static_muts(stmts); @@ -414,7 +450,8 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { &mut #ident }; ) - }).collect::<Vec<_>>(); + }) + .collect::<Vec<_>>(); quote!( #[export_name = #ident_s] @@ -429,7 +466,8 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .into() } } } @@ -505,11 +543,13 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream { pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); - assert!( - args.to_string() == "", - "`interrupt` attribute must have no arguments" - ); + 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.ident; let ident_s = ident.to_string(); @@ -519,24 +559,30 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { let stmts = block.stmts; let unsafety = f.unsafety; - assert!( - f.constness.is_none() - && f.vis == Visibility::Inherited - && f.abi.is_none() - && f.decl.inputs.is_empty() - && f.decl.generics.params.is_empty() - && f.decl.generics.where_clause.is_none() - && f.decl.variadic.is_none() - && match f.decl.output { - ReturnType::Default => true, - ReturnType::Type(_, ref ty) => match **ty { - Type::Tuple(ref tuple) => tuple.elems.is_empty(), - Type::Never(..) => true, - _ => false, - }, + let valid_signature = f.constness.is_none() + && f.vis == Visibility::Inherited + && f.abi.is_none() + && f.decl.inputs.is_empty() + && f.decl.generics.params.is_empty() + && f.decl.generics.where_clause.is_none() + && f.decl.variadic.is_none() + && match f.decl.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + Type::Never(..) => true, + _ => false, }, - "`#[interrupt]` functions must have signature `[unsafe] fn() [-> !]`" - ); + }; + + if !valid_signature { + return parse::Error::new( + fspan, + "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", + ) + .to_compile_error() + .into(); + } let (statics, stmts) = extract_static_muts(stmts); @@ -557,7 +603,8 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { &mut #ident }; ) - }).collect::<Vec<_>>(); + }) + .collect::<Vec<_>>(); let hash = random_ident(); quote!( @@ -570,7 +617,8 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } - ).into() + ) + .into() } /// Attribute to mark which function will be called at the beginning of the reset handler. @@ -600,29 +648,36 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // check the function signature - assert!( - f.constness.is_none() - && f.vis == Visibility::Inherited - && f.unsafety.is_some() - && f.abi.is_none() - && f.decl.inputs.is_empty() - && f.decl.generics.params.is_empty() - && f.decl.generics.where_clause.is_none() - && f.decl.variadic.is_none() - && match f.decl.output { - ReturnType::Default => true, - ReturnType::Type(_, ref ty) => match **ty { - Type::Tuple(ref tuple) => tuple.elems.is_empty(), - _ => false, - }, + let valid_signature = f.constness.is_none() + && f.vis == Visibility::Inherited + && f.unsafety.is_some() + && f.abi.is_none() + && f.decl.inputs.is_empty() + && f.decl.generics.params.is_empty() + && f.decl.generics.where_clause.is_none() + && f.decl.variadic.is_none() + && match f.decl.output { + ReturnType::Default => true, + ReturnType::Type(_, ref ty) => match **ty { + Type::Tuple(ref tuple) => tuple.elems.is_empty(), + _ => false, }, - "`#[pre_init]` function must have signature `unsafe fn()`" - ); + }; + + if !valid_signature { + return parse::Error::new( + f.span(), + "`#[pre_init]` function must have signature `unsafe fn()`", + ) + .to_compile_error() + .into(); + } - assert!( - args.to_string() == "", - "`pre_init` attribute must have no arguments" - ); + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } // XXX should we blacklist other attributes? let attrs = f.attrs; @@ -633,7 +688,8 @@ pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { #[export_name = "__pre_init"] #(#attrs)* pub unsafe fn #ident() #block - ).into() + ) + .into() } // Creates a random identifier @@ -663,7 +719,8 @@ fn random_ident() -> Ident { } else { ('0' as u8 + rng.gen::<u8>() % 10) as char } - }).collect::<String>(), + }) + .collect::<String>(), Span::call_site(), ) } @@ -676,11 +733,13 @@ fn extract_static_muts(stmts: Vec<Stmt>) -> (Vec<ItemStatic>, Vec<Stmt>) { let mut stmts = vec![]; while let Some(stmt) = istmts.next() { match stmt { - Stmt::Item(Item::Static(var)) => if var.mutability.is_some() { - statics.push(var); - } else { - stmts.push(Stmt::Item(Item::Static(var))); - }, + Stmt::Item(Item::Static(var)) => { + if var.mutability.is_some() { + statics.push(var); + } else { + stmts.push(Stmt::Item(Item::Static(var))); + } + } _ => { stmts.push(stmt); break; |