1extern crate proc_macro;
4
5mod vector;
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 {
87 #(#attrs)*
88 static mut #ident: #ty = #expr;
89 &mut #ident
90 }
91 }
92 })
93 .collect::<Vec<_>>();
94
95 quote::quote! (
96 #[cfg(not(any(doc, target_arch = "avr")))]
97 compile_error!(
98 "Ensure that you are using an AVR target! You may need to change \
99 directories or pass a --target flag to cargo. See
100 https://github.com/Rahix/avr-device/pull/41 for more details."
101 );
102
103 #[doc(hidden)]
104 #[export_name = "main"]
105 pub unsafe extern "C" fn #tramp_ident() {
106 #ident(
107 #(#resource_args),*
108 )
109 }
110
111 #[doc(hidden)]
112 #f
113 )
114 .into()
115}
116
117#[proc_macro_attribute]
118pub fn interrupt(
119 args: proc_macro::TokenStream,
120 input: proc_macro::TokenStream,
121) -> proc_macro::TokenStream {
122 let mut f: syn::ItemFn =
123 syn::parse(input).expect("`#[interrupt]` must be applied to a function");
124 let args: Vec<_> = args.into_iter().collect();
125
126 let fspan = f.span();
127 let ident = f.sig.ident.clone();
128 let ident_s = ident.to_string();
129
130 let chip = if let Some(tree) = args.get(0) {
131 if let proc_macro::TokenTree::Ident(ident) = tree {
132 ident.to_string()
133 } else {
134 return syn::parse::Error::new(
135 proc_macro2::Span::call_site(),
136 "#[interrupt(chip)]: chip must be an ident",
137 )
138 .to_compile_error()
139 .into();
140 }
141 } else {
142 return syn::parse::Error::new(
143 proc_macro2::Span::call_site(),
144 "#[interrupt(chip)] needs a chip argument",
145 )
146 .to_compile_error()
147 .into();
148 };
149
150 let valid_signature = f.sig.constness.is_none()
151 && f.vis == syn::Visibility::Inherited
152 && f.sig.abi.is_none()
153 && f.sig.inputs.is_empty()
154 && f.sig.generics.params.is_empty()
155 && f.sig.generics.where_clause.is_none()
156 && f.sig.variadic.is_none()
157 && match f.sig.output {
158 syn::ReturnType::Default => true,
159 syn::ReturnType::Type(_, ref ty) => match **ty {
160 syn::Type::Tuple(ref tuple) => tuple.elems.is_empty(),
161 syn::Type::Never(..) => true,
162 _ => false,
163 },
164 };
165
166 if !valid_signature {
167 return syn::parse::Error::new(
168 fspan,
169 "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
170 )
171 .to_compile_error()
172 .into();
173 }
174
175 let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) {
176 Err(e) => return e.to_compile_error().into(),
177 Ok(x) => x,
178 };
179
180 f.sig.ident = syn::Ident::new(
181 &format!("__avr_device_rt_{}", f.sig.ident),
182 proc_macro2::Span::call_site(),
183 );
184 f.sig.inputs.extend(statics.iter().map(|statik| {
185 let ident = &statik.ident;
186 let ty = &statik.ty;
187 let attrs = &statik.attrs;
188 syn::parse::<syn::FnArg>(
189 quote::quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(),
190 )
191 .unwrap()
192 }));
193 f.block.stmts = stmts;
194
195 let tramp_ident = syn::Ident::new(
196 &format!("{}_trampoline", f.sig.ident),
197 proc_macro2::Span::call_site(),
198 );
199 let ident = &f.sig.ident;
200
201 let resource_args = statics
202 .iter()
203 .map(|statik| {
204 let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone());
205 let ident = &statik.ident;
206 let ty = &statik.ty;
207 let expr = &statik.expr;
208 quote::quote! {
209 #(#cfgs)*
210 {
211 #(#attrs)*
212 static mut #ident: #ty = #expr;
213 &mut #ident
214 }
215 }
216 })
217 .collect::<Vec<_>>();
218
219 let vect = if let Some(v) = vector::lookup_vector(&chip, &ident_s) {
220 v
221 } else {
222 return syn::parse::Error::new(
223 proc_macro2::Span::call_site(),
224 &format!("Chip `{}` or interrupt `{}` unknown", chip, ident_s),
225 )
226 .to_compile_error()
227 .into();
228 };
229 let vector = format!("__vector_{}", vect);
230 let vector_ident = syn::Ident::new(&vector, proc_macro2::Span::call_site());
231 let vector_ident_s = vector_ident.to_string();
232
233 quote::quote! (
234 #[doc(hidden)]
235 #[export_name = #vector_ident_s]
236 pub unsafe extern "avr-interrupt" fn #tramp_ident() {
237 #ident(
238 #(#resource_args),*
239 )
240 }
241
242 #[doc(hidden)]
243 #f
244 )
245 .into()
246}
247
248fn extract_static_muts(
250 stmts: impl IntoIterator<Item = syn::Stmt>,
251) -> Result<(Vec<syn::ItemStatic>, Vec<syn::Stmt>), syn::parse::Error> {
252 let mut istmts = stmts.into_iter();
253
254 let mut seen = std::collections::HashSet::new();
255 let mut statics = vec![];
256 let mut stmts = vec![];
257 while let Some(stmt) = istmts.next() {
258 match stmt {
259 syn::Stmt::Item(syn::Item::Static(var)) => {
260 if var.mutability.is_some() {
261 if seen.contains(&var.ident) {
262 return Err(syn::parse::Error::new(
263 var.ident.span(),
264 format!("the name `{}` is defined multiple times", var.ident),
265 ));
266 }
267
268 seen.insert(var.ident.clone());
269 statics.push(var);
270 } else {
271 stmts.push(syn::Stmt::Item(syn::Item::Static(var)));
272 }
273 }
274 _ => {
275 stmts.push(stmt);
276 break;
277 }
278 }
279 }
280
281 stmts.extend(istmts);
282
283 Ok((statics, stmts))
284}
285
286fn extract_cfgs(attrs: Vec<syn::Attribute>) -> (Vec<syn::Attribute>, Vec<syn::Attribute>) {
287 let mut cfgs = vec![];
288 let mut not_cfgs = vec![];
289
290 for attr in attrs {
291 if eq(&attr, "cfg") {
292 cfgs.push(attr);
293 } else {
294 not_cfgs.push(attr);
295 }
296 }
297
298 (cfgs, not_cfgs)
299}
300
301fn eq(attr: &syn::Attribute, name: &str) -> bool {
303 attr.style == syn::AttrStyle::Outer && attr.path.is_ident(name)
304}