esp_hal_procmacros/
interrupt.rs1use darling::{FromMeta, ast::NestedMeta};
2use proc_macro::{Span, TokenStream};
3use proc_macro_crate::{FoundCrate, crate_name};
4use proc_macro2::Ident;
5use syn::{
6 AttrStyle,
7 Attribute,
8 ItemFn,
9 ReturnType,
10 Type,
11 parse::Error as SynError,
12 spanned::Spanned,
13};
14
15pub enum WhiteListCaller {
16 Interrupt,
17}
18
19pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
20 #[derive(Debug, FromMeta)]
21 struct MacroArgs {
22 priority: Option<syn::Expr>,
23 }
24
25 let mut f: ItemFn = syn::parse(input).expect("`#[handler]` must be applied to a function");
26 let original_span = f.span();
27
28 let attr_args = match NestedMeta::parse_meta_list(args.into()) {
29 Ok(v) => v,
30 Err(e) => {
31 return TokenStream::from(darling::Error::from(e).write_errors());
32 }
33 };
34
35 let args = match MacroArgs::from_list(&attr_args) {
36 Ok(v) => v,
37 Err(e) => {
38 return TokenStream::from(e.write_errors());
39 }
40 };
41
42 let root = Ident::new(
43 match crate_name("esp-hal") {
44 Ok(FoundCrate::Name(ref name)) => name,
45 _ => "crate",
46 },
47 Span::call_site().into(),
48 );
49
50 let priority = match args.priority {
51 Some(priority) => {
52 quote::quote!( #priority )
53 }
54 _ => {
55 quote::quote! { #root::interrupt::Priority::min() }
56 }
57 };
58
59 if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) {
62 return error;
63 }
64
65 let valid_signature = f.sig.constness.is_none()
66 && f.sig.abi.is_none()
67 && f.sig.generics.params.is_empty()
68 && f.sig.generics.where_clause.is_none()
69 && f.sig.variadic.is_none()
70 && match f.sig.output {
71 ReturnType::Default => true,
72 ReturnType::Type(_, ref ty) => match **ty {
73 Type::Tuple(ref tuple) => tuple.elems.is_empty(),
74 Type::Never(..) => true,
75 _ => false,
76 },
77 }
78 && f.sig.inputs.len() <= 1;
79
80 if !valid_signature {
81 return SynError::new(
82 f.span(),
83 "`#[handler]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`",
84 )
85 .to_compile_error()
86 .into();
87 }
88
89 f.sig.abi = syn::parse_quote_spanned!(original_span => extern "C");
90 let orig = f.sig.ident;
91 let vis = f.vis.clone();
92 f.sig.ident = Ident::new(
93 &format!("__esp_hal_internal_{orig}"),
94 proc_macro2::Span::call_site(),
95 );
96 let new = f.sig.ident.clone();
97
98 quote::quote_spanned!(original_span =>
99 #f
100
101 const _: () = {
102 core::assert!(
103 match #priority {
104 #root::interrupt::Priority::None => false,
105 _ => true,
106 },
107 "Priority::None is not supported");
108 };
109
110 #[allow(non_upper_case_globals)]
111 #vis const #orig: #root::interrupt::InterruptHandler = #root::interrupt::InterruptHandler::new(#new, #priority);
112 )
113 .into()
114}
115
116pub fn check_attr_whitelist(
117 attrs: &[Attribute],
118 caller: WhiteListCaller,
119) -> Result<(), TokenStream> {
120 let whitelist = &[
121 "doc",
122 "link_section",
123 "cfg",
124 "allow",
125 "warn",
126 "deny",
127 "forbid",
128 "cold",
129 "ram",
130 "inline",
131 ];
132
133 'o: for attr in attrs {
134 for val in whitelist {
135 if eq(attr, val) {
136 continue 'o;
137 }
138 }
139
140 let err_str = match caller {
141 WhiteListCaller::Interrupt => {
142 "this attribute is not allowed on an interrupt handler controlled by esp-hal"
143 }
144 };
145
146 return Err(SynError::new(attr.span(), err_str)
147 .to_compile_error()
148 .into());
149 }
150
151 Ok(())
152}
153
154fn eq(attr: &Attribute, name: &str) -> bool {
156 attr.style == AttrStyle::Outer && attr.path().is_ident(name)
157}