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 #[link_section = ".init9"]
117 #[export_name = "main"]
118 pub unsafe extern "C" fn #tramp_ident() {
119 #ident(
120 #(#resource_args),*
121 )
122 }
123
124 #[doc(hidden)]
125 #f
126 )
127 .into()
128}
129
130#[proc_macro_attribute]
131pub fn interrupt(
132 args: proc_macro::TokenStream,
133 input: proc_macro::TokenStream,
134) -> proc_macro::TokenStream {
135 let mut f: syn::ItemFn =
136 syn::parse(input).expect("`#[interrupt]` must be applied to a function");
137 let args: Vec<_> = args.into_iter().collect();
138
139 let fspan = f.span();
140 let ident = f.sig.ident.clone();
141
142 let chip = if let Some(tree) = args.get(0) {
143 if let proc_macro::TokenTree::Ident(ident) = tree {
144 syn::Ident::new(&ident.to_string(), fspan)
145 } else {
146 return syn::parse::Error::new(
147 proc_macro2::Span::call_site(),
148 "#[interrupt(chip)]: chip must be an ident",
149 )
150 .to_compile_error()
151 .into();
152 }
153 } else {
154 return syn::parse::Error::new(
155 proc_macro2::Span::call_site(),
156 "#[interrupt(chip)] needs a chip argument",
157 )
158 .to_compile_error()
159 .into();
160 };
161
162 let valid_signature = f.sig.constness.is_none()
163 && f.vis == syn::Visibility::Inherited
164 && f.sig.abi.is_none()
165 && f.sig.inputs.is_empty()
166 && f.sig.generics.params.is_empty()
167 && f.sig.generics.where_clause.is_none()
168 && f.sig.variadic.is_none()
169 && match f.sig.output {
170 syn::ReturnType::Default => true,
171 syn::ReturnType::Type(_, ref ty) => match **ty {
172 syn::Type::Tuple(ref tuple) => tuple.elems.is_empty(),
173 syn::Type::Never(..) => true,
174 _ => false,
175 },
176 };
177
178 if !valid_signature {
179 return syn::parse::Error::new(
180 fspan,
181 "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
182 )
183 .to_compile_error()
184 .into();
185 }
186
187 let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) {
188 Err(e) => return e.to_compile_error().into(),
189 Ok(x) => x,
190 };
191
192 f.sig.ident = syn::Ident::new(
193 &format!("__avr_device_rt_{}", f.sig.ident),
194 proc_macro2::Span::call_site(),
195 );
196 f.sig.inputs.extend(statics.iter().map(|statik| {
197 let ident = &statik.ident;
198 let ty = &statik.ty;
199 let attrs = &statik.attrs;
200 syn::parse::<syn::FnArg>(
201 quote::quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(),
202 )
203 .unwrap()
204 }));
205 f.block.stmts = stmts;
206
207 let resource_args = statics
208 .iter()
209 .map(|statik| {
210 let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
211 let ident = &statik.ident;
212 let ty = &statik.ty;
213 let expr = &statik.expr;
214 quote::quote! {
215 #(#cfgs)*
216 unsafe {
217 #(#attrs)*
218 static mut #ident: #ty = #expr;
219 &mut #ident
220 }
221 }
222 })
223 .collect::<Vec<_>>();
224
225 if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) {
226 return error;
227 }
228
229 let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
230
231 let tramp_ident = syn::Ident::new(
232 &format!("{}_trampoline", f.sig.ident),
233 proc_macro2::Span::call_site(),
234 );
235 let interrupt_ident = &f.sig.ident;
236
237 quote::quote! {
238 #(#cfgs)*
239 #(#attrs)*
240 ::avr_device::__avr_device_trampoline!(@#chip, #ident, pub extern "avr-interrupt" fn #tramp_ident() {
241 #[allow(static_mut_refs)]
242 #interrupt_ident(
243 #(#resource_args),*
244 )
245 });
246
247 #f
248 }
249 .into()
250}
251
252fn extract_static_muts(
254 stmts: impl IntoIterator<Item = syn::Stmt>,
255) -> Result<(Vec<syn::ItemStatic>, Vec<syn::Stmt>), syn::parse::Error> {
256 let mut istmts = stmts.into_iter();
257
258 let mut seen = std::collections::HashSet::new();
259 let mut statics = vec![];
260 let mut stmts = vec![];
261 while let Some(stmt) = istmts.next() {
262 match stmt {
263 syn::Stmt::Item(syn::Item::Static(var)) => {
264 if var.mutability.is_some() {
265 if seen.contains(&var.ident) {
266 return Err(syn::parse::Error::new(
267 var.ident.span(),
268 format!("the name `{}` is defined multiple times", var.ident),
269 ));
270 }
271
272 seen.insert(var.ident.clone());
273 statics.push(var);
274 } else {
275 stmts.push(syn::Stmt::Item(syn::Item::Static(var)));
276 }
277 }
278 _ => {
279 stmts.push(stmt);
280 break;
281 }
282 }
283 }
284
285 stmts.extend(istmts);
286
287 Ok((statics, stmts))
288}
289
290fn extract_cfgs(attrs: Vec<syn::Attribute>) -> (Vec<syn::Attribute>, Vec<syn::Attribute>) {
291 let mut cfgs = vec![];
292 let mut not_cfgs = vec![];
293
294 for attr in attrs {
295 if eq(&attr, "cfg") {
296 cfgs.push(attr);
297 } else {
298 not_cfgs.push(attr);
299 }
300 }
301
302 (cfgs, not_cfgs)
303}
304
305enum WhiteListCaller {
306 Entry,
307 Interrupt,
308}
309
310fn check_attr_whitelist(attrs: &[syn::Attribute], caller: WhiteListCaller) -> Result<(), proc_macro::TokenStream> {
311 let whitelist = &[
312 "doc",
313 "link_section",
314 "cfg",
315 "allow",
316 "warn",
317 "deny",
318 "forbid",
319 "cold",
320 "naked",
321 ];
322
323 'o: for attr in attrs {
324 for val in whitelist {
325 if eq(attr, val) {
326 continue 'o;
327 }
328 }
329
330 let err_str = match caller {
331 WhiteListCaller::Entry => "this attribute is not allowed on an avr-device entry point",
332 WhiteListCaller::Interrupt => {
333 "this attribute is not allowed on an interrupt handler controlled by avr-device"
334 }
335 };
336
337 return Err(syn::parse::Error::new(attr.span(), err_str)
338 .to_compile_error()
339 .into());
340 }
341
342 Ok(())
343}
344
345fn eq(attr: &syn::Attribute, name: &str) -> bool {
347 attr.style == syn::AttrStyle::Outer && attr.path.is_ident(name)
348}