1#![deny(clippy::unwrap_used)]
14#![forbid(unsafe_code)]
15
16use darling::{
17 ast::NestedMeta,
18 util::{Flag, WithOriginal},
19 Error, FromAttributes, FromMeta, Result,
20};
21use proc_macro::TokenStream;
22use proc_macro2::TokenStream as TokenStream2;
23use proc_macro_error::{abort, proc_macro_error};
24use quote::{format_ident, quote};
25use std::collections::HashMap;
26use syn::{
27 parse_macro_input, parse_str, FnArg, GenericArgument, ImplGenerics, ImplItem, ImplItemFn,
28 ItemImpl, Pat, PathArguments, ReturnType, Type, TypeGenerics, WhereClause,
29};
30
31#[derive(Debug, Clone, FromMeta)]
32#[darling(and_then = "Self::validate")]
33struct FfiMethodOptArg {
35 #[darling(rename = "self")]
36 receiver: Flag,
38 #[darling(default)]
39 ty: Option<String>,
40 #[darling(default)]
41 rename: Option<String>,
42 rest: Flag,
43}
44
45impl FfiMethodOptArg {
46 fn validate(self) -> Result<Self> {
47 if self.receiver.is_present() && self.rest.is_present() {
48 Err(Error::custom(
49 "An argument may either be self or have rest enabled.",
50 ))
51 } else if self.rest.is_present() && (self.ty.is_some() || self.rename.is_some()) {
52 Err(Error::custom(
53 "The rest argument may not specify a rename or type change",
54 ))
55 } else {
56 Ok(self)
57 }
58 }
59}
60
61#[derive(Debug, FromAttributes)]
62#[darling(
63 attributes(ffi),
64 forward_attrs(
65 cfg,
66 derive,
67 allow,
68 warn,
69 deny,
70 forbid,
71 deprecated,
72 must_use,
73 doc,
74 non_exhaustive
75 )
76)]
77struct FfiMethodOpts {
78 expect: Flag,
79 #[darling(default)]
80 visibility: Option<String>,
81 #[darling(default)]
82 name: Option<String>,
83 #[darling(multiple)]
84 arg: Vec<FfiMethodOptArg>,
85}
86
87impl FfiMethodOpts {
88 fn visibility(&self) -> TokenStream2 {
89 if let Some(ref visibility) = self.visibility {
90 match parse_str(visibility) {
91 Ok(visibility) => visibility,
92 Err(e) => Error::from(e).write_errors(),
93 }
94 } else {
95 quote!(pub)
97 }
98 }
99}
100
101struct FfiFuncRename {
102 rename: String,
103 _ty: Box<Type>,
104}
105
106struct FfiFuncArgs {
107 name: Option<TokenStream2>,
108 args: Vec<TokenStream2>,
109 renames: HashMap<usize, FfiFuncRename>,
110}
111
112#[derive(Debug)]
113struct FfiMethods<'a> {
114 ffi_self_ty: Option<Type>,
115 expect: Flag,
116 self_ty: Type,
117 self_generics: (ImplGenerics<'a>, TypeGenerics<'a>, Option<&'a WhereClause>),
118 ffi_methods: Vec<WithOriginal<FfiMethodOpts, ImplItemFn>>,
119 other_items: Vec<&'a ImplItem>,
120}
121
122impl<'a> FfiMethods<'a> {
123 fn try_from(value: &'a ItemImpl, ffi_self_ty: Option<Type>, expect: Flag) -> Result<Self> {
124 let self_generics = value.generics.split_for_impl();
125 let mut ffi_methods = Vec::new();
126 let mut other_items = Vec::new();
127 let mut errors = Vec::new();
128
129 value.items.iter().for_each(|i| {
130 if let ImplItem::Fn(ref f) = i {
131 match FfiMethodOpts::from_attributes(&f.attrs) {
132 Ok(opts) => {
133 let mut f = f.clone();
134 f.attrs
138 .retain(|a| FfiMethodOpts::from_attributes(&[a.clone()]).is_err());
139 ffi_methods.push(WithOriginal::new(opts, f));
140 }
141 Err(e) => errors.push(e),
142 }
143 } else {
144 other_items.push(i);
145 }
146 });
147
148 if !errors.is_empty() {
149 Err(Error::multiple(errors))
150 } else {
151 Ok(Self {
152 ffi_self_ty,
153 expect,
154 self_ty: *value.self_ty.clone(),
155 self_generics,
156 ffi_methods,
157 other_items,
158 })
159 }
160 }
161}
162
163impl<'a> FfiMethods<'a> {
164 fn original(&self) -> TokenStream2 {
165 let orig_ffi_methods = self
166 .ffi_methods
167 .iter()
168 .map(|m| &m.original)
169 .collect::<Vec<_>>();
170
171 let other_items = &self.other_items;
172
173 quote! {
174 #(#orig_ffi_methods)*
175 #(#other_items)*
176 }
177 }
178
179 fn ffi_return_ty(return_ty: &ReturnType, expect: bool) -> (TokenStream2, TokenStream2, bool) {
180 if expect {
181 if let ReturnType::Type(_, t) = return_ty {
182 if let Type::Path(p) = &**t {
183 if let Some(last) = p.path.segments.last() {
184 if last.ident == "Result" {
185 if let PathArguments::AngleBracketed(a) = &last.arguments {
186 return (
187 quote!(#return_ty),
188 a.args
189 .first()
190 .map(|a| quote!(-> #a))
191 .unwrap_or(quote!(#return_ty)),
192 true,
193 );
194 }
195 }
196 }
197 }
198 }
199 }
200
201 (quote!(#return_ty), quote!(#return_ty), false)
202 }
203
204 fn ffi_func_name(&self, method: &WithOriginal<FfiMethodOpts, ImplItemFn>) -> TokenStream2 {
205 method
206 .parsed
207 .name
208 .as_ref()
209 .map(|n| {
210 let name = format_ident!("{n}");
211 quote!(#name)
212 })
213 .unwrap_or({
214 let name = &method.original.sig.ident;
215 quote!(#name)
216 })
217 }
218
219 fn ffi_func_args(
220 &self,
221 method: &WithOriginal<FfiMethodOpts, ImplItemFn>,
222 ) -> Result<FfiFuncArgs> {
223 let impl_method_args = method.original.sig.inputs.iter().collect::<Vec<_>>();
224 let impl_method_args_no_receiver = method
225 .original
226 .sig
227 .inputs
228 .iter()
229 .filter(|a| !matches!(a, FnArg::Receiver(_)))
230 .cloned()
231 .collect::<Vec<_>>();
232 let mut name = None;
233 let mut args = Vec::new();
234 let mut renames = HashMap::new();
235
236 for (i, arg) in method.parsed.arg.iter().enumerate() {
237 if arg.receiver.is_present() {
238 let ty = if let Some(ref ty) = arg.ty {
239 match parse_str::<Type>(ty) {
240 Ok(ty) => quote!(#ty),
241 Err(e) => Error::from(e).write_errors(),
242 }
243 } else if let Some(ref ty) = self.ffi_self_ty {
244 quote!(#ty)
245 } else {
246 let ty = &self.self_ty;
247 quote!(#ty)
248 };
249
250 let arg_name = arg
251 .rename
252 .as_ref()
253 .map(|n| {
254 let n = format_ident!("{n}");
255 quote!(#n)
256 })
257 .unwrap_or(quote!(slf));
258 args.push(quote!(#arg_name: #ty));
259 name = Some(arg_name);
260 } else if arg.rest.is_present() {
261 let mut arg_index = i;
264
265 if name.is_none() {
266 arg_index += 1;
267 }
268
269 args.extend(
270 impl_method_args_no_receiver
271 .iter()
272 .enumerate()
273 .filter_map(|(i, a)| (i >= arg_index - 1).then_some(a))
274 .map(|a| quote!(#a)),
275 );
276 } else if args.len() <= impl_method_args_no_receiver.len() + 1 {
277 let mut arg_index = i;
280
281 if name.is_none() {
282 arg_index += 1;
283 }
284
285 let Some(FnArg::Typed(impl_method_arg_pat_type)) = impl_method_args.get(arg_index)
286 else {
287 return Err(Error::custom(
288 "Argument is not a typed argument while getting ffi function arguments",
289 ));
290 };
291
292 let ty = &impl_method_arg_pat_type.ty;
293 if let Some(ref rename) = arg.rename {
294 renames.insert(
295 i,
296 FfiFuncRename {
297 rename: rename.clone(),
298 _ty: ty.clone(),
299 },
300 );
301 args.push({
302 let rename = format_ident!("{rename}");
303 quote!(#rename: #ty)
304 });
305 } else {
306 args.push(quote!(#impl_method_arg_pat_type));
307 }
308 } else {
309 return Err(Error::custom(
310 "Argument is not a typed argument while getting ffi function arguments",
311 ));
312 }
313 }
314
315 Ok(FfiFuncArgs {
316 name,
317 args,
318 renames,
319 })
320 }
321
322 fn ffi_method_call(
323 &self,
324 method: &WithOriginal<FfiMethodOpts, ImplItemFn>,
325 ffi_receiver_name: &Option<TokenStream2>,
326 ffi_func_renames: &HashMap<usize, FfiFuncRename>,
327 need_expect: bool,
328 ) -> TokenStream2 {
329 let impl_method_args_no_receiver = method
330 .original
331 .sig
332 .inputs
333 .iter()
334 .filter(|a| !matches!(a, FnArg::Receiver(_)))
335 .cloned()
336 .collect::<Vec<_>>();
337 let Some(impl_method_receiver) = method.original.sig.receiver() else {
338 abort!(method.original, "No receiver on method");
339 };
340
341 let maybe_mut_ref = impl_method_receiver.mutability.map(|m| quote!(#m));
342 let self_ty = &self.self_ty;
343 let Some(self_name) = ffi_receiver_name else {
344 return Error::custom("No receiver name").write_errors();
345 };
346 let impl_method_name = &method.original.sig.ident;
347 let mut impl_method_call_args = Vec::new();
348 for (i, arg) in impl_method_args_no_receiver.iter().enumerate() {
349 if let Some(rename) = ffi_func_renames.get(&i) {
350 let ident = format_ident!("{}", rename.rename);
351 impl_method_call_args.push(quote!(#ident));
352 } else {
353 let FnArg::Typed(ref typed) = arg else {
354 return Error::custom(format!("Argument {i} is not a typed argument"))
355 .write_errors();
356 };
357 let Pat::Ident(ref ident) = &*typed.pat else {
358 return Error::custom("Pattern is not an identifier").write_errors();
359 };
360 let ident = &ident.ident;
361 impl_method_call_args.push(quote!(#ident));
362 }
363 }
364 let impl_maybe_expect = ((method.parsed.expect.is_present() || self.expect.is_present())
365 && need_expect)
366 .then_some({
367 let expect_message =
368 format!("Failed to execute FFI method {}", method.original.sig.ident);
369 quote!(.expect(#expect_message))
370 })
371 .unwrap_or_default();
372 quote! {
373 Into::<&#maybe_mut_ref #self_ty>::into(#self_name).#impl_method_name(
374 #(#impl_method_call_args),*
375 )#impl_maybe_expect
376 }
377 }
378
379 fn ffi_method(&self, method: &WithOriginal<FfiMethodOpts, ImplItemFn>) -> Result<TokenStream2> {
380 let ffi_func_name = self.ffi_func_name(method);
381 let ffi_func_args = self.ffi_func_args(method)?;
382
383 let (_impl_method_return_ty, ffi_func_return_ty, need_expect) = Self::ffi_return_ty(
384 &method.original.sig.output,
385 method.parsed.expect.is_present() || self.expect.is_present(),
386 );
387
388 let impl_method_args_no_receiver = method
389 .original
390 .sig
391 .inputs
392 .iter()
393 .filter(|a| !matches!(a, FnArg::Receiver(_)))
394 .cloned()
395 .collect::<Vec<_>>();
396
397 let mut impl_method_call_args = Vec::new();
398
399 for (i, arg) in impl_method_args_no_receiver.iter().enumerate() {
400 if let Some(rename) = ffi_func_args.renames.get(&i) {
401 let ident = format_ident!("{}", rename.rename);
402 impl_method_call_args.push(quote!(#ident));
403 } else {
404 let FnArg::Typed(ref typed) = arg else {
405 return Err(Error::custom(format!(
406 "Argument {i} is not a typed argument"
407 )));
408 };
409 let Pat::Ident(ref ident) = &*typed.pat else {
410 return Err(Error::custom("Pattern is not an identifier"));
411 };
412 let ident = &ident.ident;
413 impl_method_call_args.push(quote!(#ident));
414 }
415 }
416
417 let ffi_func_visibility = method.parsed.visibility();
418 let (_self_impl_genrics, self_ty_generics, self_where_clause) = &self.self_generics;
419
420 let impl_method_call = self.ffi_method_call(
421 method,
422 &ffi_func_args.name,
423 &ffi_func_args.renames,
424 need_expect,
425 );
426 let ffi_func_args = ffi_func_args.args;
427
428 Ok(quote! {
429 #[no_mangle]
430 #ffi_func_visibility extern "C" fn #ffi_func_name #self_ty_generics(
431 #(#ffi_func_args),*
432 ) #ffi_func_return_ty #self_where_clause {
433 #impl_method_call
434 }
435 })
436 }
437
438 fn ffi(&self) -> Result<TokenStream2> {
439 let methods = self
440 .ffi_methods
441 .iter()
442 .map(|m| self.ffi_method(m))
443 .collect::<Result<Vec<_>>>()?;
444
445 Ok(quote! {
446 #(#methods)*
447 })
448 }
449}
450
451#[derive(Debug, FromMeta)]
452struct FfiOpts {
454 #[darling(default, rename = "mod_name")]
455 name: Option<String>,
458 #[darling(default)]
459 visibility: Option<String>,
461 #[darling(default)]
462 self_ty: Option<String>,
465 expect: Flag,
467 from_ptr: Flag,
469 from_any_ptr: Flag,
471}
472
473impl FfiOpts {
474 fn generate(&self, input: &ItemImpl) -> Result<TokenStream> {
476 let methods = match FfiMethods::try_from(
477 input,
478 self.self_ty
479 .as_ref()
480 .and_then(|s| parse_str::<Type>(s).ok()),
481 self.expect,
482 ) {
483 Ok(o) => o,
484 Err(e) => return Err(e),
485 };
486
487 let original_impl = self.original_impl(input, methods.original());
488 let maybe_from_ptr_impl = self.maybe_from_ptr_impl(input);
489 let maybe_from_any_ptr_impl = self.maybe_from_any_ptr_impl(input);
490 let ffi_mod = self.ffi_mod(input, methods.ffi()?);
491
492 Ok(quote! {
493 #original_impl
494
495 #maybe_from_ptr_impl
496
497 #maybe_from_any_ptr_impl
498
499 #ffi_mod
500
501 }
502 .into())
503 }
504
505 fn input_name(&self, input: &ItemImpl) -> TokenStream2 {
506 let Type::Path(p) = &*input.self_ty else {
507 abort!(input, "Self type must be path");
508 };
509
510 let Some(last) = p.path.segments.last() else {
511 abort!(input, "Self type must have segments");
512 };
513
514 match last.arguments {
515 PathArguments::None => {
516 let name = &input.self_ty;
517 quote!(#name)
518 }
519 PathArguments::AngleBracketed(_) => {
520 let last_ident = &last.ident;
521 let mut segments = p.path.segments.iter().cloned().collect::<Vec<_>>();
522 segments.pop();
523 let segments = segments.iter().map(|s| quote!(#s)).collect::<Vec<_>>();
524 quote!(#(#segments)::*#last_ident)
525 }
526 PathArguments::Parenthesized(_) => {
527 abort!(input, "Parenthesized path arguments are not allowed here")
528 }
529 }
530 }
531
532 fn self_type_generics(&self, input: &ItemImpl) -> Vec<GenericArgument> {
533 let Type::Path(p) = &*input.self_ty else {
534 abort!(input, "Self type must be path");
535 };
536
537 let Some(last) = p.path.segments.last() else {
538 abort!(input, "Self type must have segments");
539 };
540
541 match last.arguments {
542 PathArguments::None => {
543 vec![]
544 }
545 PathArguments::AngleBracketed(ref a) => a.args.clone().into_iter().collect::<Vec<_>>(),
546 PathArguments::Parenthesized(_) => {
547 abort!(input, "Parenthesized path arguments are not allowed here")
548 }
549 }
550 }
551
552 fn original_impl(&self, input: &ItemImpl, original: TokenStream2) -> TokenStream2 {
553 let maybe_trait = input.trait_.as_ref().map(|(not, path, f)| {
556 let maybe_not = not.map(|not| quote!(#not)).unwrap_or_default();
557 quote!(#maybe_not #path #f)
558 });
559
560 let impl_generics = &input.generics.params.iter().collect::<Vec<_>>();
561 let where_clause = &input.generics.where_clause;
562 let input_name = self.input_name(input);
563 let self_type_generics = self.self_type_generics(input);
564
565 let maybe_impl_generics = if impl_generics.is_empty() {
566 quote!()
567 } else {
568 quote!(<#(#impl_generics),*>)
569 };
570
571 let maybe_self_type_generics = if self_type_generics.is_empty() {
572 quote!()
573 } else {
574 quote!(<#(#self_type_generics),*>)
575 };
576
577 quote! {
578 impl #maybe_impl_generics #maybe_trait #input_name #maybe_self_type_generics #where_clause {
579 #original
580 }
581 }
582 }
583
584 fn maybe_from_ptr_impl(&self, input: &ItemImpl) -> TokenStream2 {
585 let input_name = self.input_name(input);
586 let self_type_generics = self.self_type_generics(input);
587 let impl_generics_from = self
588 .self_type_generics(input)
589 .iter()
590 .map(|g| quote!(#g))
591 .collect::<Vec<_>>();
592 self.from_ptr.is_present()
593 .then_some(
594 self
595 .self_ty
596 .as_ref()
597 .and_then(|st| {
598 parse_str(st).ok().map(|stp: Type| {
599 quote! {
600 impl<#(#impl_generics_from),*> From<#stp> for &'static mut #input_name<#(#self_type_generics),*> {
601 fn from(value: #stp) -> Self {
602 let ptr: *mut #input_name <#(#self_type_generics),*>= value as *mut #input_name <#(#self_type_generics),*>;
603 unsafe { &mut *ptr }
604 }
605 }
606
607 impl<#(#impl_generics_from),*> From<#stp> for &'static #input_name <#(#self_type_generics),*> {
608 fn from(value: #stp) -> Self {
609 let ptr: *mut #input_name <#(#self_type_generics),*> = value as *mut #input_name <#(#self_type_generics),*>;
610 unsafe { &*ptr }
611 }
612 }
613 }
614 })
615 })
616 )
617 .flatten()
618 .unwrap_or_default()
619 }
620
621 fn maybe_from_any_ptr_impl(&self, input: &ItemImpl) -> TokenStream2 {
622 let input_name = self.input_name(input);
623 let self_type_generics = self.self_type_generics(input);
624 let impl_generics_from = self
625 .self_type_generics(input)
626 .iter()
627 .map(|g| quote!(#g))
628 .collect::<Vec<_>>();
629 self.from_any_ptr.is_present().then_some(quote! {
630 impl<#(#impl_generics_from),*> From<*mut T> for &'static mut #input_name<#(#self_type_generics),*> {
631 fn from(value: *mut T) -> Self {
632 let ptr: *mut #input_name <#(#self_type_generics),*>= value as *mut #input_name <#(#self_type_generics),*>;
633 unsafe { &mut *ptr }
634 }
635 }
636
637 impl<#(#impl_generics_from),*> From<*mut T> for &'static #input_name<#(#self_type_generics),*> {
638 fn from(value: *mut T) -> Self {
639 let ptr: *mut #input_name <#(#self_type_generics),*> = value as *mut #input_name <#(#self_type_generics),*>;
640 unsafe { &*ptr }
641 }
642 }
643
644 impl<#(#impl_generics_from),*> From<*const T> for &'static #input_name<#(#self_type_generics),*> {
645 fn from(value: *const T) -> Self {
646 let ptr: *const #input_name <#(#self_type_generics),*> = value as *const #input_name <#(#self_type_generics),*>;
647 unsafe { &*ptr }
648 }
649 }
650 }).unwrap_or_default()
651 }
652
653 fn ffi_mod(&self, input: &ItemImpl, ffi: TokenStream2) -> TokenStream2 {
654 let visibility = if let Some(ref visibility) = self.visibility {
655 match parse_str(visibility) {
656 Ok(visibility) => visibility,
657 Err(e) => Error::from(e).write_errors(),
658 }
659 } else {
660 quote!(pub)
662 };
663
664 let name = self.module_name(input);
665
666 quote! {
667 #visibility mod #name {
668 use super::*;
669 #ffi
670 }
671 }
672 }
673
674 fn module_name(&self, input: &ItemImpl) -> TokenStream2 {
675 if let Some(name) = self.name.as_ref().map(|n| {
676 let name = format_ident!("{n}");
677 quote!(#name)
678 }) {
679 name
680 } else {
681 let Type::Path(path) = input.self_ty.as_ref() else {
682 abort!(input, "Implementation self type is not a path");
683 };
684
685 let Some(name) = path.path.segments.first() else {
686 abort!(path, "Path has no segments");
687 };
688
689 let ffi_mod_name = format_ident!("{}", name.ident.to_string().to_ascii_lowercase());
690
691 quote!(#ffi_mod_name)
692 }
693 }
694}
695
696#[proc_macro_attribute]
697#[proc_macro_error]
698pub fn ffi(args: TokenStream, input: TokenStream) -> TokenStream {
833 let meta = match NestedMeta::parse_meta_list(args.into()) {
834 Ok(o) => o,
835 Err(e) => return TokenStream::from(Error::from(e).write_errors()),
836 };
837
838 let options = match FfiOpts::from_list(&meta) {
839 Ok(o) => o,
840 Err(e) => return TokenStream::from(e.write_errors()),
841 };
842
843 let impl_item = parse_macro_input!(input as ItemImpl);
844
845 options
846 .generate(&impl_item)
847 .unwrap_or_else(|e| TokenStream::from(e.write_errors()))
848}