llvm_plugin_macros/
lib.rs1use proc_macro::TokenStream;
2
3use proc_macro2::Span;
4use proc_macro2::TokenStream as TokenStream2;
5
6use quote::{format_ident, quote, quote_spanned};
7
8use syn::ItemFn;
9use syn::{AttributeArgs, Error};
10
11#[proc_macro_attribute]
42pub fn plugin(attrs: TokenStream, input: TokenStream) -> TokenStream {
43 match plugin_impl(attrs, input) {
44 Ok(ts) => ts.into(),
45 Err(e) => {
46 let msg = e.to_string();
47 quote_spanned! { e.span() => fn error() { std::compile_error!(#msg) } }.into()
48 }
49 }
50}
51
52fn plugin_impl(attrs: TokenStream, input: TokenStream) -> syn::Result<TokenStream2> {
53 let args = syn::parse_macro_input::parse(attrs)?;
54 let (name, version) = match parse_plugin_args(args) {
55 Some(parsed) => parsed?,
56 None => return Err(Error::new(Span::call_site(), "`plugin` attr missing args")),
57 };
58
59 let func = syn::parse::<ItemFn>(input)?;
60 let registrar_name = &func.sig.ident;
61 let registrar_name_sys = format_ident!("{}_sys", registrar_name);
62
63 let name = name + "\0";
64 let version = version + "\0";
65
66 Ok(quote! {
67 #func
68
69 extern "C" fn #registrar_name_sys(builder: *mut std::ffi::c_void) {
70 let mut builder = unsafe { llvm_plugin::PassBuilder::from_raw(builder) };
71 #registrar_name(&mut builder);
72 }
73
74 #[no_mangle]
75 extern "C" fn llvmGetPassPluginInfo() -> llvm_plugin::PassPluginLibraryInfo {
76 llvm_plugin::PassPluginLibraryInfo {
77 api_version: llvm_plugin::get_llvm_plugin_api_version__(),
78 plugin_name: #name.as_ptr(),
79 plugin_version: #version.as_ptr(),
80 plugin_registrar: #registrar_name_sys,
81 }
82 }
83 })
84}
85
86fn parse_plugin_args(args: AttributeArgs) -> Option<syn::Result<(String, String)>> {
87 let mut args_iter = args.iter();
88
89 let arg = args_iter.next()?;
90 let name = match arg {
91 syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
92 path,
93 lit: syn::Lit::Str(s),
94 ..
95 })) if path.is_ident("name") => s.value(),
96 _ => {
97 return Some(Err(Error::new_spanned(
98 arg,
99 "expected arg `name=\"value\"`",
100 )))
101 }
102 };
103
104 let arg = args_iter.next()?;
105 let version = match arg {
106 syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
107 path,
108 lit: syn::Lit::Str(s),
109 ..
110 })) if path.is_ident("version") => s.value(),
111 _ => {
112 return Some(Err(Error::new_spanned(
113 arg,
114 "expected arg `version=\"value\"`",
115 )))
116 }
117 };
118
119 Some(Ok((name, version)))
120}