abi_stable_derive/
sabi_extern_fn_impl.rs
1use std::mem;
4
5use as_derive_utils::return_spanned_err;
6
7use proc_macro::TokenStream as TokenStream1;
8use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree};
9
10use quote::{quote, ToTokens};
11
12use syn::{Expr, ItemFn};
13
14use crate::parse_or_compile_err;
15
16#[doc(hidden)]
17pub fn sabi_extern_fn(attr: TokenStream1, item: TokenStream1) -> TokenStream1 {
18 parse_or_compile_err(item, move |item| sabi_extern_fn_inner(attr.into(), item)).into()
19}
20
21#[cfg(test)]
22pub(crate) fn sabi_extern_fn_str(attr: &str, item: &str) -> Result<TokenStream2, syn::Error> {
23 syn::parse_str(item).and_then(move |item| {
24 let attr = syn::parse_str::<TokenStream2>(attr)?;
25 sabi_extern_fn_inner(attr, item)
26 })
27}
28
29#[derive(Debug, Copy, Clone, PartialEq)]
31pub enum WithEarlyReturn {
32 No,
33 Yes,
34}
35
36pub(crate) fn convert_to_sabi_extern_fn(with_early_return: WithEarlyReturn, item: &mut ItemFn) {
38 let no_early_return = match with_early_return {
39 WithEarlyReturn::No => Some(quote!( no_early_return; )),
40 WithEarlyReturn::Yes => None,
41 };
42
43 item.sig.abi = Some(syn::Abi {
44 extern_token: Default::default(),
45 name: Some(syn::LitStr::new("C", Span::call_site())),
46 });
47
48 let statements = mem::take(&mut item.block.stmts);
49
50 let x = quote! {
51 ::abi_stable::extern_fn_panic_handling!(
52 #no_early_return
53
54 #(#statements)*
55 )
56 };
57 let x = Expr::Verbatim(x);
58 let x = syn::Stmt::Expr(x);
59 let x = vec![x];
60 item.block.stmts = x;
61}
62
63fn sabi_extern_fn_inner(attr: TokenStream2, mut item: ItemFn) -> Result<TokenStream2, syn::Error> {
64 let with_early_return = match attr.into_iter().next() {
65 Some(TokenTree::Ident(ref ident)) if ident == "no_early_return" => WithEarlyReturn::No,
66 Some(tt) => return_spanned_err!(tt, "Unrecognized `#[sabi_extern_fn]` parameter",),
67 None => WithEarlyReturn::Yes,
68 };
69
70 convert_to_sabi_extern_fn(with_early_return, &mut item);
71
72 Ok(item.into_token_stream())
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_output() {
81 let list = vec![
82 (
83 "",
84 r##"
85 pub fn hello()->RString{
86 println!("{}",HELLO);
87 println!(",");
88 println!("{}",WORLD);
89 }
90 "##,
91 quote!(
92 pub extern "C" fn hello() -> RString {
93 ::abi_stable::extern_fn_panic_handling!(
94 println!("{}",HELLO);
95 println!(",");
96 println!("{}",WORLD);
97 )
98 }
99 ),
100 ),
101 (
102 "no_early_return",
103 r##"
104 pub(crate) extern "Rust" fn hello()->RStr<'_>{
105 println!("{}",HELLO);
106 println!(",");
107 println!("{}",WORLD);
108 "stuff".into()
109 }
110 "##,
111 quote!(
112 pub(crate) extern "C" fn hello() -> RStr<'_> {
113 ::abi_stable::extern_fn_panic_handling!(
114 no_early_return;
115 println!("{}",HELLO);
116 println!(",");
117 println!("{}",WORLD);
118 "stuff".into()
119 )
120 }
121 ),
122 ),
123 ];
124
125 for (attr, item, expected) in list {
126 assert_eq!(
127 sabi_extern_fn_str(attr, item).unwrap().to_string(),
128 expected.to_string()
129 );
130 }
131 }
132}