use proc_macro::TokenStream;
use proc_macro2::Span;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote, quote_spanned};
use syn::ItemFn;
use syn::{AttributeArgs, Error};
#[proc_macro_attribute]
pub fn plugin(attrs: TokenStream, input: TokenStream) -> TokenStream {
match plugin_impl(attrs, input) {
Ok(ts) => ts.into(),
Err(e) => {
let msg = e.to_string();
quote_spanned! { e.span() => fn error() { std::compile_error!(#msg) } }.into()
}
}
}
fn plugin_impl(attrs: TokenStream, input: TokenStream) -> syn::Result<TokenStream2> {
let args = syn::parse_macro_input::parse(attrs)?;
let (name, version) = match parse_plugin_args(args) {
Some(parsed) => parsed?,
None => return Err(Error::new(Span::call_site(), "`plugin` attr missing args")),
};
let func = syn::parse::<ItemFn>(input)?;
let registrar_name = &func.sig.ident;
let registrar_name_sys = format_ident!("{}_sys", registrar_name);
let name = name + "\0";
let version = version + "\0";
Ok(quote! {
#func
extern "C" fn #registrar_name_sys(builder: *mut std::ffi::c_void) {
let mut builder = unsafe { llvm_plugin::PassBuilder::from_raw(builder) };
#registrar_name(&mut builder);
}
#[no_mangle]
extern "C" fn llvmGetPassPluginInfo() -> llvm_plugin::PassPluginLibraryInfo {
llvm_plugin::PassPluginLibraryInfo {
api_version: llvm_plugin::get_llvm_plugin_api_version__(),
plugin_name: #name.as_ptr(),
plugin_version: #version.as_ptr(),
plugin_registrar: #registrar_name_sys,
}
}
})
}
fn parse_plugin_args(args: AttributeArgs) -> Option<syn::Result<(String, String)>> {
let mut args_iter = args.iter();
let arg = args_iter.next()?;
let name = match arg {
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(s),
..
})) if path.is_ident("name") => s.value(),
_ => {
return Some(Err(Error::new_spanned(
arg,
"expected arg `name=\"value\"`",
)))
}
};
let arg = args_iter.next()?;
let version = match arg {
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
path,
lit: syn::Lit::Str(s),
..
})) if path.is_ident("version") => s.value(),
_ => {
return Some(Err(Error::new_spanned(
arg,
"expected arg `version=\"value\"`",
)))
}
};
Some(Ok((name, version)))
}