1 | //! Internal implementation details of `cortex-m-rt`. |
2 | //! |
3 | //! Do not use this crate directly. |
4 | |
5 | extern crate proc_macro; |
6 | |
7 | use proc_macro::TokenStream; |
8 | use proc_macro2::Span; |
9 | use quote::quote; |
10 | use std::iter; |
11 | use std::{collections::HashSet, fmt::Display}; |
12 | use syn::{ |
13 | parse::{self, Parse}, |
14 | parse_macro_input, |
15 | spanned::Spanned, |
16 | AttrStyle, Attribute, FnArg, Ident, Item, ItemFn, ItemStatic, ReturnType, Stmt, Type, |
17 | Visibility, |
18 | }; |
19 | |
20 | #[proc_macro_attribute ] |
21 | pub fn entry (args: TokenStream, input: TokenStream) -> TokenStream { |
22 | let mut f = parse_macro_input!(input as ItemFn); |
23 | |
24 | // check the function signature |
25 | let valid_signature = f.sig.constness.is_none() |
26 | && f.vis == Visibility::Inherited |
27 | && f.sig.abi.is_none() |
28 | && f.sig.inputs.is_empty() |
29 | && f.sig.generics.params.is_empty() |
30 | && f.sig.generics.where_clause.is_none() |
31 | && f.sig.variadic.is_none() |
32 | && match f.sig.output { |
33 | ReturnType::Default => false, |
34 | ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)), |
35 | }; |
36 | |
37 | if !valid_signature { |
38 | return parse::Error::new( |
39 | f.span(), |
40 | "`#[entry]` function must have signature `[unsafe] fn() -> !`" , |
41 | ) |
42 | .to_compile_error() |
43 | .into(); |
44 | } |
45 | |
46 | if !args.is_empty() { |
47 | return parse::Error::new(Span::call_site(), "This attribute accepts no arguments" ) |
48 | .to_compile_error() |
49 | .into(); |
50 | } |
51 | |
52 | // XXX should we blacklist other attributes? |
53 | let (statics, stmts) = match extract_static_muts(f.block.stmts) { |
54 | Err(e) => return e.to_compile_error().into(), |
55 | Ok(x) => x, |
56 | }; |
57 | |
58 | f.sig.ident = Ident::new(&format!("__cortex_m_rt_ {}" , f.sig.ident), Span::call_site()); |
59 | f.sig.inputs.extend(statics.iter().map(|statik| { |
60 | let ident = &statik.ident; |
61 | let ty = &statik.ty; |
62 | let attrs = &statik.attrs; |
63 | |
64 | // Note that we use an explicit `'static` lifetime for the entry point arguments. This makes |
65 | // it more flexible, and is sound here, since the entry will not be called again, ever. |
66 | syn::parse::<FnArg>( |
67 | quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &'static mut #ty).into(), |
68 | ) |
69 | .unwrap() |
70 | })); |
71 | f.block.stmts = stmts; |
72 | |
73 | let tramp_ident = Ident::new(&format!(" {}_trampoline" , f.sig.ident), Span::call_site()); |
74 | let ident = &f.sig.ident; |
75 | |
76 | let resource_args = statics |
77 | .iter() |
78 | .map(|statik| { |
79 | let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone()); |
80 | let ident = &statik.ident; |
81 | let ty = &statik.ty; |
82 | let expr = &statik.expr; |
83 | quote! { |
84 | #(#cfgs)* |
85 | { |
86 | #(#attrs)* |
87 | static mut #ident: #ty = #expr; |
88 | unsafe { &mut #ident } |
89 | } |
90 | } |
91 | }) |
92 | .collect::<Vec<_>>(); |
93 | |
94 | if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Entry) { |
95 | return error; |
96 | } |
97 | |
98 | let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); |
99 | |
100 | quote!( |
101 | #(#cfgs)* |
102 | #(#attrs)* |
103 | #[doc(hidden)] |
104 | #[export_name = "main" ] |
105 | pub unsafe extern "C" fn #tramp_ident() { |
106 | #[allow(static_mut_refs)] |
107 | #ident( |
108 | #(#resource_args),* |
109 | ) |
110 | } |
111 | |
112 | #f |
113 | ) |
114 | .into() |
115 | } |
116 | |
117 | #[derive (Debug, PartialEq)] |
118 | enum Exception { |
119 | DefaultHandler, |
120 | HardFault(HardFaultArgs), |
121 | NonMaskableInt, |
122 | Other, |
123 | } |
124 | |
125 | impl Display for Exception { |
126 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
127 | match self { |
128 | Exception::DefaultHandler => write!(f, "`DefaultHandler`" ), |
129 | Exception::HardFault(_) => write!(f, "`HardFault` handler" ), |
130 | Exception::NonMaskableInt => write!(f, "`NonMaskableInt` handler" ), |
131 | Exception::Other => write!(f, "Other exception handler" ), |
132 | } |
133 | } |
134 | } |
135 | |
136 | #[derive (Debug, PartialEq)] |
137 | struct HardFaultArgs { |
138 | trampoline: bool, |
139 | } |
140 | |
141 | impl Default for HardFaultArgs { |
142 | fn default() -> Self { |
143 | Self { trampoline: true } |
144 | } |
145 | } |
146 | |
147 | impl Parse for HardFaultArgs { |
148 | fn parse(input: parse::ParseStream) -> syn::Result<Self> { |
149 | let mut items = Vec::new(); |
150 | // Read a list of `ident = value,` |
151 | loop { |
152 | if input.is_empty() { |
153 | break; |
154 | } |
155 | |
156 | let name = input.parse::<Ident>()?; |
157 | input.parse::<syn::Token!(=)>()?; |
158 | let value = input.parse::<syn::Lit>()?; |
159 | |
160 | items.push((name, value)); |
161 | |
162 | if input.is_empty() { |
163 | break; |
164 | } |
165 | |
166 | input.parse::<syn::Token!(,)>()?; |
167 | } |
168 | |
169 | let mut args = Self::default(); |
170 | |
171 | for (name, value) in items { |
172 | match name.to_string().as_str() { |
173 | "trampoline" => match value { |
174 | syn::Lit::Bool(val) => { |
175 | args.trampoline = val.value(); |
176 | } |
177 | _ => { |
178 | return Err(syn::Error::new_spanned( |
179 | value, |
180 | "Not a valid value. `trampoline` takes a boolean literal" , |
181 | )) |
182 | } |
183 | }, |
184 | _ => { |
185 | return Err(syn::Error::new_spanned(name, "Not a valid argument name" )); |
186 | } |
187 | } |
188 | } |
189 | |
190 | Ok(args) |
191 | } |
192 | } |
193 | |
194 | #[proc_macro_attribute ] |
195 | pub fn exception (args: TokenStream, input: TokenStream) -> TokenStream { |
196 | let mut f = parse_macro_input!(input as ItemFn); |
197 | |
198 | if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Exception) { |
199 | return error; |
200 | } |
201 | |
202 | let fspan = f.span(); |
203 | let ident = f.sig.ident.clone(); |
204 | |
205 | let ident_s = ident.to_string(); |
206 | let exn = match &*ident_s { |
207 | "DefaultHandler" => { |
208 | if !args.is_empty() { |
209 | return parse::Error::new(Span::call_site(), "This attribute accepts no arguments" ) |
210 | .to_compile_error() |
211 | .into(); |
212 | } |
213 | Exception::DefaultHandler |
214 | } |
215 | "HardFault" => Exception::HardFault(parse_macro_input!(args)), |
216 | "NonMaskableInt" => { |
217 | if !args.is_empty() { |
218 | return parse::Error::new(Span::call_site(), "This attribute accepts no arguments" ) |
219 | .to_compile_error() |
220 | .into(); |
221 | } |
222 | Exception::NonMaskableInt |
223 | } |
224 | // NOTE that at this point we don't check if the exception is available on the target (e.g. |
225 | // MemoryManagement is not available on Cortex-M0) |
226 | "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall" |
227 | | "DebugMonitor" | "PendSV" | "SysTick" => { |
228 | if !args.is_empty() { |
229 | return parse::Error::new(Span::call_site(), "This attribute accepts no arguments" ) |
230 | .to_compile_error() |
231 | .into(); |
232 | } |
233 | |
234 | Exception::Other |
235 | } |
236 | _ => { |
237 | return parse::Error::new(ident.span(), "This is not a valid exception name" ) |
238 | .to_compile_error() |
239 | .into(); |
240 | } |
241 | }; |
242 | |
243 | if f.sig.unsafety.is_none() { |
244 | match exn { |
245 | Exception::DefaultHandler | Exception::HardFault(_) | Exception::NonMaskableInt => { |
246 | // These are unsafe to define. |
247 | let name = format!(" {}" , exn); |
248 | return parse::Error::new(ident.span(), format_args!("defining a {} is unsafe and requires an `unsafe fn` (see the cortex-m-rt docs)" , name)) |
249 | .to_compile_error() |
250 | .into(); |
251 | } |
252 | Exception::Other => {} |
253 | } |
254 | } |
255 | |
256 | // Emit a reference to the `Exception` variant corresponding to our exception. |
257 | // This will fail compilation when the target doesn't have that exception. |
258 | let assertion = match exn { |
259 | Exception::Other => { |
260 | quote! { |
261 | const _: () = { |
262 | let _ = ::cortex_m_rt::Exception::#ident; |
263 | }; |
264 | } |
265 | } |
266 | _ => quote!(), |
267 | }; |
268 | |
269 | let handler = match exn { |
270 | Exception::DefaultHandler => { |
271 | let valid_signature = f.sig.constness.is_none() |
272 | && f.vis == Visibility::Inherited |
273 | && f.sig.abi.is_none() |
274 | && f.sig.inputs.len() == 1 |
275 | && f.sig.generics.params.is_empty() |
276 | && f.sig.generics.where_clause.is_none() |
277 | && f.sig.variadic.is_none() |
278 | && match f.sig.output { |
279 | ReturnType::Default => true, |
280 | ReturnType::Type(_, ref ty) => match **ty { |
281 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), |
282 | Type::Never(..) => true, |
283 | _ => false, |
284 | }, |
285 | }; |
286 | |
287 | if !valid_signature { |
288 | return parse::Error::new( |
289 | fspan, |
290 | "`DefaultHandler` must have signature `unsafe fn(i16) [-> !]`" , |
291 | ) |
292 | .to_compile_error() |
293 | .into(); |
294 | } |
295 | |
296 | f.sig.ident = Ident::new(&format!("__cortex_m_rt_ {}" , f.sig.ident), Span::call_site()); |
297 | let tramp_ident = Ident::new(&format!(" {}_trampoline" , f.sig.ident), Span::call_site()); |
298 | let ident = &f.sig.ident; |
299 | |
300 | let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); |
301 | |
302 | quote!( |
303 | #(#cfgs)* |
304 | #(#attrs)* |
305 | #[doc(hidden)] |
306 | #[export_name = #ident_s] |
307 | pub unsafe extern "C" fn #tramp_ident() { |
308 | extern crate core; |
309 | |
310 | const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; |
311 | |
312 | let irqn = unsafe { (core::ptr::read_volatile(SCB_ICSR) & 0x1FF) as i16 - 16 }; |
313 | |
314 | #ident(irqn) |
315 | } |
316 | |
317 | #f |
318 | ) |
319 | } |
320 | Exception::HardFault(args) => { |
321 | let valid_signature = f.sig.constness.is_none() |
322 | && f.vis == Visibility::Inherited |
323 | && f.sig.abi.is_none() |
324 | && if args.trampoline { |
325 | f.sig.inputs.len() == 1 |
326 | && match &f.sig.inputs[0] { |
327 | FnArg::Typed(arg) => match arg.ty.as_ref() { |
328 | Type::Reference(r) => { |
329 | r.lifetime.is_none() && r.mutability.is_none() |
330 | } |
331 | _ => false, |
332 | }, |
333 | _ => false, |
334 | } |
335 | } else { |
336 | f.sig.inputs.is_empty() |
337 | } |
338 | && f.sig.generics.params.is_empty() |
339 | && f.sig.generics.where_clause.is_none() |
340 | && f.sig.variadic.is_none() |
341 | && match f.sig.output { |
342 | ReturnType::Default => false, |
343 | ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)), |
344 | }; |
345 | |
346 | if !valid_signature { |
347 | return parse::Error::new( |
348 | fspan, |
349 | if args.trampoline { |
350 | "`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`" |
351 | } else { |
352 | "`HardFault` handler must have signature `unsafe fn() -> !`" |
353 | }, |
354 | ) |
355 | .to_compile_error() |
356 | .into(); |
357 | } |
358 | |
359 | f.sig.ident = Ident::new(&format!("__cortex_m_rt_ {}" , f.sig.ident), Span::call_site()); |
360 | let tramp_ident = Ident::new(&format!(" {}_trampoline" , f.sig.ident), Span::call_site()); |
361 | |
362 | if args.trampoline { |
363 | let ident = &f.sig.ident; |
364 | |
365 | let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); |
366 | |
367 | quote!( |
368 | #(#cfgs)* |
369 | #(#attrs)* |
370 | #[doc(hidden)] |
371 | #[export_name = "_HardFault" ] |
372 | // Only emit link_section when building for embedded targets, |
373 | // because some hosted platforms (used to check the build) |
374 | // cannot handle the long link section names. |
375 | #[cfg_attr(target_os = "none" , link_section = ".HardFault.user" )] |
376 | unsafe extern "C" fn #tramp_ident(frame: &::cortex_m_rt::ExceptionFrame) { |
377 | #ident(frame) |
378 | } |
379 | |
380 | #f |
381 | |
382 | // HardFault exceptions are bounced through this trampoline which grabs the stack pointer at |
383 | // the time of the exception and passes it to the user's HardFault handler in r0. |
384 | // Depending on the stack mode in EXC_RETURN, fetches stack from either MSP or PSP. |
385 | core::arch::global_asm!( |
386 | ".cfi_sections .debug_frame |
387 | .section .HardFaultTrampoline, \"ax \" |
388 | .global HardFault |
389 | .type HardFault,%function |
390 | .thumb_func |
391 | .cfi_startproc |
392 | HardFault:" , |
393 | "mov r0, lr |
394 | movs r1, #4 |
395 | tst r0, r1 |
396 | bne 0f |
397 | mrs r0, MSP |
398 | b _HardFault |
399 | 0: |
400 | mrs r0, PSP |
401 | b _HardFault" , |
402 | ".cfi_endproc |
403 | .size HardFault, . - HardFault" , |
404 | ); |
405 | ) |
406 | } else { |
407 | quote!( |
408 | #[doc(hidden)] |
409 | #[export_name = "_HardFault" ] |
410 | unsafe extern "C" fn #tramp_ident() { |
411 | // This trampoline has no function except making the compiler diagnostics better. |
412 | } |
413 | |
414 | #[export_name = "HardFault" ] |
415 | // Only emit link_section when building for embedded targets, |
416 | // because some hosted platforms (used to check the build) |
417 | // cannot handle the long link section names. |
418 | #[cfg_attr(target_os = "none" , link_section = ".HardFault.user" )] |
419 | #f |
420 | ) |
421 | } |
422 | } |
423 | Exception::NonMaskableInt | Exception::Other => { |
424 | let valid_signature = f.sig.constness.is_none() |
425 | && f.vis == Visibility::Inherited |
426 | && f.sig.abi.is_none() |
427 | && f.sig.inputs.is_empty() |
428 | && f.sig.generics.params.is_empty() |
429 | && f.sig.generics.where_clause.is_none() |
430 | && f.sig.variadic.is_none() |
431 | && match f.sig.output { |
432 | ReturnType::Default => true, |
433 | ReturnType::Type(_, ref ty) => match **ty { |
434 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), |
435 | Type::Never(..) => true, |
436 | _ => false, |
437 | }, |
438 | }; |
439 | |
440 | if !valid_signature { |
441 | return parse::Error::new( |
442 | fspan, |
443 | "`#[exception]` handlers other than `DefaultHandler` and `HardFault` must have \ |
444 | signature `[unsafe] fn() [-> !]`" , |
445 | ) |
446 | .to_compile_error() |
447 | .into(); |
448 | } |
449 | |
450 | let (statics, stmts) = match extract_static_muts(f.block.stmts) { |
451 | Err(e) => return e.to_compile_error().into(), |
452 | Ok(x) => x, |
453 | }; |
454 | |
455 | f.sig.ident = Ident::new(&format!("__cortex_m_rt_ {}" , f.sig.ident), Span::call_site()); |
456 | f.sig.inputs.extend(statics.iter().map(|statik| { |
457 | let ident = &statik.ident; |
458 | let ty = &statik.ty; |
459 | let attrs = &statik.attrs; |
460 | syn::parse::<FnArg>( |
461 | quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into(), |
462 | ) |
463 | .unwrap() |
464 | })); |
465 | f.block.stmts = iter::once( |
466 | syn::parse2(quote! {{ |
467 | // check that this exception actually exists |
468 | ::cortex_m_rt::exception::#ident; |
469 | }}) |
470 | .unwrap(), |
471 | ) |
472 | .chain(stmts) |
473 | .collect(); |
474 | |
475 | let tramp_ident = Ident::new(&format!(" {}_trampoline" , f.sig.ident), Span::call_site()); |
476 | let ident = &f.sig.ident; |
477 | |
478 | let resource_args = statics |
479 | .iter() |
480 | .map(|statik| { |
481 | let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone()); |
482 | let ident = &statik.ident; |
483 | let ty = &statik.ty; |
484 | let expr = &statik.expr; |
485 | quote! { |
486 | #(#cfgs)* |
487 | { |
488 | #(#attrs)* |
489 | static mut #ident: #ty = #expr; |
490 | unsafe { &mut #ident } |
491 | } |
492 | } |
493 | }) |
494 | .collect::<Vec<_>>(); |
495 | |
496 | let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); |
497 | |
498 | quote!( |
499 | #(#cfgs)* |
500 | #(#attrs)* |
501 | #[doc(hidden)] |
502 | #[export_name = #ident_s] |
503 | pub unsafe extern "C" fn #tramp_ident() { |
504 | #[allow(static_mut_refs)] |
505 | #ident( |
506 | #(#resource_args),* |
507 | ) |
508 | } |
509 | |
510 | #f |
511 | ) |
512 | } |
513 | }; |
514 | |
515 | quote!( |
516 | #assertion |
517 | #handler |
518 | ) |
519 | .into() |
520 | } |
521 | |
522 | #[proc_macro_attribute ] |
523 | pub fn interrupt (args: TokenStream, input: TokenStream) -> TokenStream { |
524 | let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function" ); |
525 | |
526 | if !args.is_empty() { |
527 | return parse::Error::new(Span::call_site(), "This attribute accepts no arguments" ) |
528 | .to_compile_error() |
529 | .into(); |
530 | } |
531 | |
532 | let fspan = f.span(); |
533 | let ident = f.sig.ident.clone(); |
534 | let ident_s = ident.to_string(); |
535 | |
536 | // XXX should we blacklist other attributes? |
537 | |
538 | let valid_signature = f.sig.constness.is_none() |
539 | && f.vis == Visibility::Inherited |
540 | && f.sig.abi.is_none() |
541 | && f.sig.inputs.is_empty() |
542 | && f.sig.generics.params.is_empty() |
543 | && f.sig.generics.where_clause.is_none() |
544 | && f.sig.variadic.is_none() |
545 | && match f.sig.output { |
546 | ReturnType::Default => true, |
547 | ReturnType::Type(_, ref ty) => match **ty { |
548 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), |
549 | Type::Never(..) => true, |
550 | _ => false, |
551 | }, |
552 | }; |
553 | |
554 | if !valid_signature { |
555 | return parse::Error::new( |
556 | fspan, |
557 | "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`" , |
558 | ) |
559 | .to_compile_error() |
560 | .into(); |
561 | } |
562 | |
563 | let (statics, stmts) = match extract_static_muts(f.block.stmts.iter().cloned()) { |
564 | Err(e) => return e.to_compile_error().into(), |
565 | Ok(x) => x, |
566 | }; |
567 | |
568 | f.sig.ident = Ident::new(&format!("__cortex_m_rt_ {}" , f.sig.ident), Span::call_site()); |
569 | f.sig.inputs.extend(statics.iter().map(|statik| { |
570 | let ident = &statik.ident; |
571 | let ty = &statik.ty; |
572 | let attrs = &statik.attrs; |
573 | syn::parse::<FnArg>(quote!(#[allow(non_snake_case)] #(#attrs)* #ident: &mut #ty).into()) |
574 | .unwrap() |
575 | })); |
576 | f.block.stmts = iter::once( |
577 | syn::parse2(quote! {{ |
578 | // Check that this interrupt actually exists |
579 | interrupt::#ident; |
580 | }}) |
581 | .unwrap(), |
582 | ) |
583 | .chain(stmts) |
584 | .collect(); |
585 | |
586 | let tramp_ident = Ident::new(&format!(" {}_trampoline" , f.sig.ident), Span::call_site()); |
587 | let ident = &f.sig.ident; |
588 | |
589 | let resource_args = statics |
590 | .iter() |
591 | .map(|statik| { |
592 | let (ref cfgs, ref attrs) = extract_cfgs(statik.attrs.clone()); |
593 | let ident = &statik.ident; |
594 | let ty = &statik.ty; |
595 | let expr = &statik.expr; |
596 | quote! { |
597 | #(#cfgs)* |
598 | { |
599 | #(#attrs)* |
600 | static mut #ident: #ty = #expr; |
601 | unsafe { &mut #ident } |
602 | } |
603 | } |
604 | }) |
605 | .collect::<Vec<_>>(); |
606 | |
607 | if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::Interrupt) { |
608 | return error; |
609 | } |
610 | |
611 | let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone()); |
612 | |
613 | quote!( |
614 | #(#cfgs)* |
615 | #(#attrs)* |
616 | #[doc(hidden)] |
617 | #[export_name = #ident_s] |
618 | pub unsafe extern "C" fn #tramp_ident() { |
619 | #[allow(static_mut_refs)] |
620 | #ident( |
621 | #(#resource_args),* |
622 | ) |
623 | } |
624 | |
625 | #f |
626 | ) |
627 | .into() |
628 | } |
629 | |
630 | #[proc_macro_attribute ] |
631 | pub fn pre_init (args: TokenStream, input: TokenStream) -> TokenStream { |
632 | let f = parse_macro_input!(input as ItemFn); |
633 | |
634 | // check the function signature |
635 | let valid_signature = f.sig.constness.is_none() |
636 | && f.vis == Visibility::Inherited |
637 | && f.sig.unsafety.is_some() |
638 | && f.sig.abi.is_none() |
639 | && f.sig.inputs.is_empty() |
640 | && f.sig.generics.params.is_empty() |
641 | && f.sig.generics.where_clause.is_none() |
642 | && f.sig.variadic.is_none() |
643 | && match f.sig.output { |
644 | ReturnType::Default => true, |
645 | ReturnType::Type(_, ref ty) => match **ty { |
646 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), |
647 | _ => false, |
648 | }, |
649 | }; |
650 | |
651 | if !valid_signature { |
652 | return parse::Error::new( |
653 | f.span(), |
654 | "`#[pre_init]` function must have signature `unsafe fn()`" , |
655 | ) |
656 | .to_compile_error() |
657 | .into(); |
658 | } |
659 | |
660 | if !args.is_empty() { |
661 | return parse::Error::new(Span::call_site(), "This attribute accepts no arguments" ) |
662 | .to_compile_error() |
663 | .into(); |
664 | } |
665 | |
666 | if let Err(error) = check_attr_whitelist(&f.attrs, WhiteListCaller::PreInit) { |
667 | return error; |
668 | } |
669 | |
670 | // XXX should we blacklist other attributes? |
671 | let attrs = f.attrs; |
672 | let ident = f.sig.ident; |
673 | let block = f.block; |
674 | |
675 | quote!( |
676 | #[export_name = "__pre_init" ] |
677 | #[allow(missing_docs)] // we make a private fn public, which can trigger this lint |
678 | #(#attrs)* |
679 | pub unsafe fn #ident() #block |
680 | ) |
681 | .into() |
682 | } |
683 | |
684 | /// Extracts `static mut` vars from the beginning of the given statements |
685 | fn extract_static_muts( |
686 | stmts: impl IntoIterator<Item = Stmt>, |
687 | ) -> Result<(Vec<ItemStatic>, Vec<Stmt>), parse::Error> { |
688 | let mut istmts = stmts.into_iter(); |
689 | |
690 | let mut seen = HashSet::new(); |
691 | let mut statics = vec![]; |
692 | let mut stmts = vec![]; |
693 | for stmt in istmts.by_ref() { |
694 | match stmt { |
695 | Stmt::Item(Item::Static(var)) => match var.mutability { |
696 | syn::StaticMutability::Mut(_) => { |
697 | if seen.contains(&var.ident) { |
698 | return Err(parse::Error::new( |
699 | var.ident.span(), |
700 | format!("the name ` {}` is defined multiple times" , var.ident), |
701 | )); |
702 | } |
703 | |
704 | seen.insert(var.ident.clone()); |
705 | statics.push(var); |
706 | } |
707 | _ => stmts.push(Stmt::Item(Item::Static(var))), |
708 | }, |
709 | _ => { |
710 | stmts.push(stmt); |
711 | break; |
712 | } |
713 | } |
714 | } |
715 | |
716 | stmts.extend(istmts); |
717 | |
718 | Ok((statics, stmts)) |
719 | } |
720 | |
721 | fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) { |
722 | let mut cfgs: Vec = vec![]; |
723 | let mut not_cfgs: Vec = vec![]; |
724 | |
725 | for attr: Attribute in attrs { |
726 | if eq(&attr, name:"cfg" ) { |
727 | cfgs.push(attr); |
728 | } else { |
729 | not_cfgs.push(attr); |
730 | } |
731 | } |
732 | |
733 | (cfgs, not_cfgs) |
734 | } |
735 | |
736 | enum WhiteListCaller { |
737 | Entry, |
738 | Exception, |
739 | Interrupt, |
740 | PreInit, |
741 | } |
742 | |
743 | fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<(), TokenStream> { |
744 | let whitelist = &[ |
745 | "doc" , |
746 | "link_section" , |
747 | "cfg" , |
748 | "allow" , |
749 | "warn" , |
750 | "deny" , |
751 | "forbid" , |
752 | "cold" , |
753 | "naked" , |
754 | "expect" , |
755 | ]; |
756 | |
757 | 'o: for attr in attrs { |
758 | for val in whitelist { |
759 | if eq(attr, val) { |
760 | continue 'o; |
761 | } |
762 | } |
763 | |
764 | let err_str = match caller { |
765 | WhiteListCaller::Entry => "this attribute is not allowed on a cortex-m-rt entry point" , |
766 | WhiteListCaller::Exception => { |
767 | "this attribute is not allowed on an exception handler controlled by cortex-m-rt" |
768 | } |
769 | WhiteListCaller::Interrupt => { |
770 | "this attribute is not allowed on an interrupt handler controlled by cortex-m-rt" |
771 | } |
772 | WhiteListCaller::PreInit => { |
773 | "this attribute is not allowed on a pre-init controlled by cortex-m-rt" |
774 | } |
775 | }; |
776 | |
777 | return Err(parse::Error::new(attr.span(), err_str) |
778 | .to_compile_error() |
779 | .into()); |
780 | } |
781 | |
782 | Ok(()) |
783 | } |
784 | |
785 | /// Returns `true` if `attr.path` matches `name` |
786 | fn eq(attr: &Attribute, name: &str) -> bool { |
787 | attr.style == AttrStyle::Outer && attr.path().is_ident(name) |
788 | } |
789 | |