mik32_rt_macros/
lib.rs

1use proc_macro2::Span;
2use syn::{ItemFn, ReturnType, Type, Visibility, parse, parse_macro_input, spanned::Spanned};
3
4use proc_macro::TokenStream;
5use quote::quote;
6
7#[proc_macro_attribute]
8pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
9    let f = parse_macro_input!(input as ItemFn);
10
11    // check the function arguments
12    if !f.sig.inputs.is_empty() {
13        return parse::Error::new(
14            f.sig.inputs.last().unwrap().span(),
15            "`#[entry]` function accepts no arguments",
16        )
17        .to_compile_error()
18        .into();
19    }
20
21    // check the function signature
22    let valid_signature = f.sig.constness.is_none()
23        && f.sig.asyncness.is_none()
24        && f.vis == Visibility::Inherited
25        && f.sig.abi.is_none()
26        && f.sig.generics.params.is_empty()
27        && f.sig.generics.where_clause.is_none()
28        && f.sig.variadic.is_none()
29        && match f.sig.output {
30            ReturnType::Default => false,
31            ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
32        };
33
34    if !valid_signature {
35        return parse::Error::new(
36            f.span(),
37            "`#[entry]` function must have signature `[unsafe] fn() -> !`",
38        )
39        .to_compile_error()
40        .into();
41    }
42
43    if !args.is_empty() {
44        return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
45            .to_compile_error()
46            .into();
47    }
48
49    // XXX should we blacklist other attributes?
50    let attrs = f.attrs;
51    let unsafety = f.sig.unsafety;
52    let args = f.sig.inputs;
53    let stmts = f.block.stmts;
54
55    quote!(
56        #[allow(non_snake_case)]
57        #[unsafe(export_name = "__start_rust")]
58        #(#attrs)*
59        pub #unsafety fn __risc_v_rt__main(#args) -> ! {
60            #(#stmts)*
61        }
62    )
63    .into()
64}