1extern crate proc_macro;
6
7use syn::spanned::Spanned;
8
9#[proc_macro_attribute]
10pub fn entry(
11 args: proc_macro::TokenStream,
12 input: proc_macro::TokenStream,
13) -> proc_macro::TokenStream {
14 let mut f = syn::parse_macro_input!(input as syn::ItemFn);
15
16 let valid_signature = f.sig.constness.is_none()
18 && f.vis == syn::Visibility::Inherited
19 && f.sig.abi.is_none()
20 && f.sig.inputs.is_empty()
21 && f.sig.generics.params.is_empty()
22 && f.sig.generics.where_clause.is_none()
23 && f.sig.variadic.is_none()
24 && match f.sig.output {
25 syn::ReturnType::Default => false,
26 syn::ReturnType::Type(_, ref ty) => matches!(**ty, syn::Type::Never(_)),
27 };
28
29 if !valid_signature {
30 return syn::parse::Error::new(
31 f.span(),
32 "`#[entry]` function must have signature `[unsafe] fn() -> !`",
33 )
34 .to_compile_error()
35 .into();
36 }
37
38 if !args.is_empty() {
39 return syn::parse::Error::new(
40 proc_macro2::Span::call_site(),
41 "This attribute accepts no arguments",
42 )
43 .to_compile_error()
44 .into();
45 }
46
47 let (statics, stmts) = match extract_static_muts(f.block.stmts) {
48 Err(e) => return e.to_compile_error().into(),
49 Ok(x) => x,
50 };
51
52 f.sig.ident = syn::Ident::new(
54 &format!("__avr_device_rt_{}", f.sig.ident),
55 proc_macro2::Span::call_site(),
56 );
57 f.sig.inputs.extend(statics.iter().map(|statik| {
58 let ident = &statik.ident;
59 let ty = &statik.ty;
60 let attrs = &statik.attrs;
61
62 syn::parse::<syn::FnArg>(
65 quote::quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &'static mut #ty).into(),
66 )
67 .unwrap()
68 }));
69 f.block.stmts = stmts;
70
71 let tramp_ident = syn::Ident::new(
72 &format!("{}_trampoline", f.sig.ident),
73 proc_macro2::Span::call_site(),
74 );
75 let ident = &f.sig.ident;
76
77 let resource_args = statics
78 .iter()
79 .map(|statik| {
80 let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
81 let ident = &statik.ident;
82 let ty = &statik.ty;
83 let expr = &statik.expr;
84 quote::quote! {
85 #(#cfgs)*
86 unsafe {
87 #(#attrs)*
88 static mut #ident: #ty = #expr;
89 &mut #ident
90 }
91 }
92 })
93 .collect::<Vec<_>>();
94
95 if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Entry) {
96 return error;
97 }
98
99 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
100
101 quote::quote! (
102 #[cfg(not(any(doc, target_arch = "avr")))]
103 compile_error!(
104 "Ensure that you are using an AVR target! You may need to change \
105 directories or pass a --target flag to cargo. See
106 https://github.com/Rahix/avr-device/pull/41 for more details."
107 );
108
109 #(#cfgs)*
110 #(#attrs)*
111 #[doc(hidden)]
112 #[export_name = "main"]
113 pub unsafe extern "C" fn #tramp_ident() {
114 #ident(
115 #(#resource_args),*
116 )
117 }
118
119 #[doc(hidden)]
120 #f
121 )
122 .into()
123}
124
125#[proc_macro_attribute]
126pub fn interrupt(
127 args: proc_macro::TokenStream,
128 input: proc_macro::TokenStream,
129) -> proc_macro::TokenStream {
130 let mut f: syn::ItemFn =
131 syn::parse(input).expect("`#[interrupt]` must be applied to a function");
132 let args: Vec<_> = args.into_iter().collect();
133
134 let fspan = f.span();
135 let ident = f.sig.ident.clone();
136
137 let chip = if let Some(tree) = args.get(0) {
138 if let proc_macro::TokenTree::Ident(ident) = tree {
139 syn::Ident::new(&ident.to_string(), fspan)
140 } else {
141 return syn::parse::Error::new(
142 proc_macro2::Span::call_site(),
143 "#[interrupt(chip)]: chip must be an ident",
144 )
145 .to_compile_error()
146 .into();
147 }
148 } else {
149 return syn::parse::Error::new(
150 proc_macro2::Span::call_site(),
151 "#[interrupt(chip)] needs a chip argument",
152 )
153 .to_compile_error()
154 .into();
155 };
156
157 let valid_signature = f.sig.constness.is_none()
158 && f.vis == syn::Visibility::Inherited
159 && f.sig.abi.is_none()
160 && f.sig.inputs.is_empty()
161 && f.sig.generics.params.is_empty()
162 && f.sig.generics.where_clause.is_none()
163 && f.sig.variadic.is_none()
164 && match f.sig.output {
165 syn::ReturnType::Default => true,
166 syn::ReturnType::Type(_, ref ty) => match **ty {
167 syn::Type::Tuple(ref tuple) => tuple.elems.is_empty(),
168 syn::Type::Never(..) => true,
169 _ => false,
170 },
171 };
172
173 if !valid_signature {
174 return syn::parse::Error::new(
175 fspan,
176 "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
177 )
178 .to_compile_error()
179 .into();
180 }
181
182 let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) {
183 Err(e) => return e.to_compile_error().into(),
184 Ok(x) => x,
185 };
186
187 f.sig.ident = syn::Ident::new(
188 &format!("__avr_device_rt_{}", f.sig.ident),
189 proc_macro2::Span::call_site(),
190 );
191 f.sig.inputs.extend(statics.iter().map(|statik| {
192 let ident = &statik.ident;
193 let ty = &statik.ty;
194 let attrs = &statik.attrs;
195 syn::parse::<syn::FnArg>(
196 quote::quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(),
197 )
198 .unwrap()
199 }));
200 f.block.stmts = stmts;
201
202 let resource_args = statics
203 .iter()
204 .map(|statik| {
205 let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
206 let ident = &statik.ident;
207 let ty = &statik.ty;
208 let expr = &statik.expr;
209 quote::quote! {
210 #(#cfgs)*
211 unsafe {
212 #(#attrs)*
213 static mut #ident: #ty = #expr;
214 &mut #ident
215 }
216 }
217 })
218 .collect::<Vec<_>>();
219
220 if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) {
221 return error;
222 }
223
224 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
225
226 let tramp_ident = syn::Ident::new(
227 &format!("{}_trampoline", f.sig.ident),
228 proc_macro2::Span::call_site(),
229 );
230 let interrupt_ident = &f.sig.ident;
231
232 quote::quote! {
233 #(#cfgs)*
234 #(#attrs)*
235 ::avr_device::__avr_device_trampoline!(@#chip, #ident, pub extern "avr-interrupt" fn #tramp_ident() {
236 #[allow(static_mut_refs)]
237 #interrupt_ident(
238 #(#resource_args),*
239 )
240 });
241
242 #f
243 }
244 .into()
245}
246
247fn extract_static_muts(
249 stmts: impl IntoIterator<Item = syn::Stmt>,
250) -> Result<(Vec<syn::ItemStatic>, Vec<syn::Stmt>), syn::parse::Error> {
251 let mut istmts = stmts.into_iter();
252
253 let mut seen = std::collections::HashSet::new();
254 let mut statics = vec![];
255 let mut stmts = vec![];
256 while let Some(stmt) = istmts.next() {
257 match stmt {
258 syn::Stmt::Item(syn::Item::Static(var)) => {
259 if var.mutability.is_some() {
260 if seen.contains(&var.ident) {
261 return Err(syn::parse::Error::new(
262 var.ident.span(),
263 format!("the name `{}` is defined multiple times", var.ident),
264 ));
265 }
266
267 seen.insert(var.ident.clone());
268 statics.push(var);
269 } else {
270 stmts.push(syn::Stmt::Item(syn::Item::Static(var)));
271 }
272 }
273 _ => {
274 stmts.push(stmt);
275 break;
276 }
277 }
278 }
279
280 stmts.extend(istmts);
281
282 Ok((statics, stmts))
283}
284
285fn extract_cfgs(attrs: Vec<syn::Attribute>) -> (Vec<syn::Attribute>, Vec<syn::Attribute>) {
286 let mut cfgs = vec![];
287 let mut not_cfgs = vec![];
288
289 for attr in attrs {
290 if eq(&attr, "cfg") {
291 cfgs.push(attr);
292 } else {
293 not_cfgs.push(attr);
294 }
295 }
296
297 (cfgs, not_cfgs)
298}
299
300enum WhiteListCaller {
301 Entry,
302 Interrupt,
303}
304
305fn check_attr_whitelist(attrs: &[syn::Attribute], caller: WhiteListCaller) -> Result<(), proc_macro::TokenStream> {
306 let whitelist = &[
307 "doc",
308 "link_section",
309 "cfg",
310 "allow",
311 "warn",
312 "deny",
313 "forbid",
314 "cold",
315 "naked",
316 ];
317
318 'o: for attr in attrs {
319 for val in whitelist {
320 if eq(attr, val) {
321 continue 'o;
322 }
323 }
324
325 let err_str = match caller {
326 WhiteListCaller::Entry => "this attribute is not allowed on an avr-device entry point",
327 WhiteListCaller::Interrupt => {
328 "this attribute is not allowed on an interrupt handler controlled by avr-device"
329 }
330 };
331
332 return Err(syn::parse::Error::new(attr.span(), err_str)
333 .to_compile_error()
334 .into());
335 }
336
337 Ok(())
338}
339
340fn eq(attr: &syn::Attribute, name: &str) -> bool {
342 attr.style == syn::AttrStyle::Outer && attr.path.is_ident(name)
343}