1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use crate::util::is_associated_fn;
use quote::quote;
use syn::{parse_quote, spanned::Spanned, Error, Ident, ItemFn, Result, Signature, Visibility};

pub fn process_safe_inner(mut item: ItemFn) -> Result<Vec<ItemFn>> {
    let safe_inner_span = {
        if let Some((idx, attr)) = item
            .attrs
            .iter()
            .enumerate()
            .find(|(_, attr)| **attr == parse_quote! { #[safe_inner] })
        {
            if item.sig.unsafety.is_none() {
                Err(Error::new(
                    attr.span(),
                    "#[safe_inner] may only be used on unsafe fn",
                ))
            } else {
                let span = attr.span();
                item.attrs.remove(idx);
                Ok(Some(span))
            }
        } else {
            Ok(None)
        }
    }?;
    let associated = is_associated_fn(&mut item);
    if let Some(safe_inner_span) = safe_inner_span {
        // create safe function
        // copy #[cfg] attributes
        let attrs = item
            .attrs
            .iter()
            .filter_map(|attr| {
                if let Ok(meta) = attr.parse_meta() {
                    if *meta.path() == parse_quote! { cfg } {
                        Some(attr.clone())
                    } else {
                        None
                    }
                } else {
                    None
                }
            })
            .chain(std::iter::once(parse_quote! { #[inline(always)] }))
            .collect();
        let safe_fn = ItemFn {
            attrs,
            vis: Visibility::Inherited,
            sig: Signature {
                unsafety: None,
                ident: Ident::new(&format!("__safe_inner_{}", item.sig.ident), safe_inner_span),
                ..item.sig.clone()
            },
            block: item.block,
        };

        // create unsafe function
        let (unsafe_sig, args) = crate::util::normalize_signature(&item.sig);
        let maybe_await = item.sig.asyncness.map(|_| crate::util::await_tokens());
        let maybe_self = if associated {
            quote! { Self:: }
        } else {
            Default::default()
        };
        let safe_ident = &safe_fn.sig.ident;
        let fn_params = crate::util::fn_params(&unsafe_sig);
        let unsafe_fn = ItemFn {
            block: parse_quote! {
                {
                    #maybe_self#safe_ident::<#(#fn_params),*>(#(#args),*)#maybe_await
                }
            },
            sig: unsafe_sig,
            ..item
        };
        Ok(vec![unsafe_fn, safe_fn])
    } else {
        Ok(vec![item])
    }
}