use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{format_ident, quote};
use syn::{Error, Item, parse_macro_input};
#[proc_macro_attribute]
pub fn register_ctor(attr: TokenStream, function: TokenStream) -> TokenStream {
if !attr.is_empty() {
return Error::new(
Span::call_site(),
"expect an empty attribute: `#[register_ctor]`",
)
.to_compile_error()
.into();
}
let item: Item = parse_macro_input!(function as Item);
if let Item::Fn(func) = item {
let name = &func.sig.ident;
let name_str = name.to_string();
let name_ident = format_ident!("_INIT_{}", name_str);
let output = &func.sig.output;
if let syn::ReturnType::Type(_, _) = output {
return Error::new(
Span::call_site(),
"expect no return value for the constructor function",
)
.to_compile_error()
.into();
}
let inputs = &func.sig.inputs;
if !inputs.is_empty() {
return Error::new(
Span::call_site(),
"expect no input arguments for the constructor function",
)
.to_compile_error()
.into();
}
let block = &func.block;
quote! {
#[unsafe(link_section = ".init_array")]
#[used]
#[allow(non_upper_case_globals)]
static #name_ident: extern "C" fn() = #name;
#[unsafe(no_mangle)]
#[allow(non_upper_case_globals)]
pub extern "C" fn #name() {
#block
}
}
.into()
} else {
Error::new(Span::call_site(), "expect a function to be registered")
.to_compile_error()
.into()
}
}