relay_ffi_macros/
lib.rs

1//! Macros for [`relay-ffi`].
2//!
3//! [`relay-ffi`]: ../relay_ffi/index.html
4
5#![warn(missing_docs)]
6#![doc(
7    html_logo_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png",
8    html_favicon_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png"
9)]
10#![allow(clippy::derive_partial_eq_without_eq)]
11
12use proc_macro::TokenStream;
13use quote::ToTokens;
14use syn::fold::Fold;
15
16struct CatchUnwind;
17
18impl CatchUnwind {
19    fn fold(&mut self, input: TokenStream) -> TokenStream {
20        let f = syn::parse(input).expect("#[catch_unwind] can only be applied to functions");
21        self.fold_item_fn(f).to_token_stream().into()
22    }
23}
24
25impl Fold for CatchUnwind {
26    fn fold_item_fn(&mut self, i: syn::ItemFn) -> syn::ItemFn {
27        if i.sig.unsafety.is_none() {
28            panic!("#[catch_unwind] requires `unsafe fn`");
29        }
30
31        let inner = i.block;
32        let folded = quote::quote! {{
33            ::relay_ffi::__internal::catch_errors(|| {
34                let __ret = #inner;
35
36                #[allow(unreachable_code)]
37                Ok(__ret)
38            })
39        }};
40
41        let block = Box::new(syn::parse2(folded).unwrap());
42        syn::ItemFn { block, ..i }
43    }
44}
45
46/// Captures errors and panics in a thread-local on `unsafe` functions.
47///
48/// See [`relay-ffi` documentation] for more information.
49///
50/// # Examples
51///
52/// ```ignore
53/// use relay_ffi::catch_unwind;
54///
55/// #[no_mangle]
56/// #[catch_unwind]
57/// pub unsafe extern "C" fn run_ffi() -> i32 {
58///     "invalid".parse()?
59/// }
60/// ```
61///
62/// [`relay-ffi` documentation]: ../relay_ffi/index.html
63#[proc_macro_attribute]
64pub fn catch_unwind(_attr: TokenStream, item: TokenStream) -> TokenStream {
65    CatchUnwind.fold(item)
66}