Skip to main content

noexcept_impl/
lib.rs

1#![cfg_attr(not(check_cfg), allow(unexpected_cfgs))]
2#![allow(
3    clippy::doc_markdown,
4    clippy::match_same_arms,
5    clippy::missing_panics_doc,
6    clippy::uninlined_format_args
7)]
8#![cfg_attr(all(test, exhaustive), feature(non_exhaustive_omitted_patterns_lint))]
9
10use proc_macro::TokenStream;
11use proc_macro2::{Span, TokenStream as TokenStream2};
12use quote::quote;
13use syn::parse::{Error, Nothing, Result};
14use syn::{
15    parse_quote, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path, PathArguments,
16    ReturnType, Token, Type, TypeInfer, TypeParamBound,
17};
18
19#[proc_macro_attribute]
20pub fn abort_on_panic(args: TokenStream, input: TokenStream) -> TokenStream {
21    let args = TokenStream2::from(args);
22    let input = TokenStream2::from(input);
23    let expanded = match parse(args, input.clone()) {
24        Ok(function) => expand_abort_on_panic(function),
25        Err(parse_error) => {
26            let compile_error = parse_error.to_compile_error();
27            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&compile_error, &mut _s);
    ::quote::ToTokens::to_tokens(&input, &mut _s);
    _s
}quote!(#compile_error #input)
28        }
29    };
30    TokenStream::from(expanded)
31}
32
33fn parse(args: TokenStream2, input: TokenStream2) -> Result<ItemFn> {
34    let function: ItemFn = syn::parse2(input)?;
35    let _: Nothing = syn::parse2::<Nothing>(args)?;
36    if function.sig.asyncness.is_some() {
37        return Err(Error::new(
38            Span::call_site(),
39            "abort_on_panic attribute on async fn is not supported",
40        ));
41    }
42    Ok(function)
43}
44
45// Convert `Path<impl Trait>` to `Path<_>`
46fn make_impl_trait_wild(ret: &mut Type) {
47    match ret {
48        #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
49        Type::ImplTrait(impl_trait) => {
50            *ret = Type::Infer(TypeInfer {
51                attrs: Vec::new(),
52                underscore_token: ::syn::token::UnderscoreToken![_](impl_trait.impl_token.span),
53            });
54        }
55        Type::Array(ret) => make_impl_trait_wild(&mut ret.elem),
56        Type::Group(ret) => make_impl_trait_wild(&mut ret.elem),
57        Type::Paren(ret) => make_impl_trait_wild(&mut ret.elem),
58        Type::Path(ret) => make_impl_trait_wild_in_path(&mut ret.path),
59        Type::Ptr(ret) => make_impl_trait_wild(&mut ret.elem),
60        Type::Reference(ret) => make_impl_trait_wild(&mut ret.elem),
61        Type::Slice(ret) => make_impl_trait_wild(&mut ret.elem),
62        Type::TraitObject(ret) => {
63            for bound in &mut ret.bounds {
64                if let TypeParamBound::Trait(bound) = bound {
65                    make_impl_trait_wild_in_path(&mut bound.path);
66                }
67            }
68        }
69        Type::Tuple(ret) => ret.elems.iter_mut().for_each(make_impl_trait_wild),
70        Type::FnPtr(_) | Type::Infer(_) | Type::Macro(_) | Type::Never(_) | Type::Verbatim(_) => {}
71        _ => {}
72    }
73}
74
75fn make_impl_trait_wild_in_path(path: &mut Path) {
76    for segment in &mut path.segments {
77        if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments {
78            for arg in &mut bracketed.args {
79                if let GenericArgument::Type(arg) = arg {
80                    make_impl_trait_wild(arg);
81                }
82            }
83        }
84    }
85}
86
87fn expand_abort_on_panic(mut function: ItemFn) -> TokenStream2 {
88    let mut move_self = None;
89    let mut arg_pat = Vec::new();
90    let mut arg_val = Vec::new();
91    for (i, input) in function.sig.inputs.iter_mut().enumerate() {
92        let numbered = Ident::new(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("__arg{0}", i))
    })format!("__arg{}", i), Span::call_site());
93        match input {
94            FnArg::Typed(PatType { pat, .. })
95                if match pat.as_ref() {
96                    Pat::Ident(pat) => pat.ident != "self",
97                    _ => true,
98                } =>
99            {
100                arg_pat.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&pat, &mut _s);
    _s
}quote!(#pat));
101                arg_val.push({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&numbered, &mut _s);
    _s
}quote!(#numbered));
102                *pat = ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_ident(&mut _s, "mut");
        ::quote::ToTokens::to_tokens(&numbered, &mut _s);
        _s
    })parse_quote!(mut #numbered);
103            }
104            FnArg::Typed(_) | FnArg::Receiver(_) => {
105                move_self = Some({
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "if");
    ::quote::__private::push_ident(&mut _s, "false");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "loop");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                ::quote::__private::TokenStream::new());
            ::quote::__private::push_pound(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "allow");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "unreachable_code");
                            _s
                        });
                    _s
                });
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "let");
                    ::quote::__private::push_ident(&mut _s, "__self");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "self");
                    ::quote::__private::push_semi(&mut _s);
                    _s
                });
            _s
        });
    _s
}quote! {
106                    if false {
107                        loop {}
108                        #[allow(unreachable_code)]
109                        {
110                            let __self = self;
111                        }
112                    }
113                });
114            }
115        }
116    }
117
118    let ret = match &function.sig.output {
119        ReturnType::Default => {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_rarrow(&mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        ::quote::__private::TokenStream::new());
    _s
}quote!(-> ()),
120        ReturnType::Type(arrow, output) => {
121            let mut output = output.clone();
122            make_impl_trait_wild(&mut output);
123            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&arrow, &mut _s);
    ::quote::ToTokens::to_tokens(&output, &mut _s);
    _s
}quote!(#arrow #output)
124        }
125    };
126    let stmts = function.block.stmts;
127    *function.block = ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_group(&mut _s,
            ::quote::__private::Delimiter::Brace,
            {
                let mut _s = ::quote::__private::TokenStream::new();
                ::quote::__private::push_ident(&mut _s, "let");
                ::quote::__private::push_ident(&mut _s, "__guard");
                ::quote::__private::push_eq(&mut _s);
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "noexcept");
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "__private");
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "AbortOnDrop");
                ::quote::__private::push_semi(&mut _s);
                ::quote::__private::push_ident(&mut _s, "let");
                ::quote::__private::push_ident(&mut _s, "__result");
                ::quote::__private::push_eq(&mut _s);
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Parenthesis,
                    {
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::push_ident(&mut _s, "move");
                        ::quote::__private::push_or_or(&mut _s);
                        ::quote::ToTokens::to_tokens(&ret, &mut _s);
                        ::quote::__private::push_group(&mut _s,
                            ::quote::__private::Delimiter::Brace,
                            {
                                let mut _s = ::quote::__private::TokenStream::new();
                                ::quote::ToTokens::to_tokens(&move_self, &mut _s);
                                {
                                    use ::quote::__private::ext::*;
                                    let has_iter = ::quote::__private::HasIterator::<false>;
                                    #[allow(unused_mut)]
                                    let (mut arg_pat, i) = arg_pat.quote_into_iter();
                                    let has_iter = has_iter | i;
                                    #[allow(unused_mut)]
                                    let (mut arg_val, i) = arg_val.quote_into_iter();
                                    let has_iter = has_iter | i;
                                    <_ as
                                            ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                    while true {
                                        let arg_pat =
                                            match arg_pat.next() {
                                                Some(_x) => ::quote::__private::RepInterp(_x),
                                                None => break,
                                            };
                                        let arg_val =
                                            match arg_val.next() {
                                                Some(_x) => ::quote::__private::RepInterp(_x),
                                                None => break,
                                            };
                                        ::quote::__private::push_ident(&mut _s, "let");
                                        ::quote::ToTokens::to_tokens(&arg_pat, &mut _s);
                                        ::quote::__private::push_eq(&mut _s);
                                        ::quote::ToTokens::to_tokens(&arg_val, &mut _s);
                                        ::quote::__private::push_semi(&mut _s);
                                    }
                                }
                                {
                                    use ::quote::__private::ext::*;
                                    let has_iter = ::quote::__private::HasIterator::<false>;
                                    #[allow(unused_mut)]
                                    let (mut stmts, i) = stmts.quote_into_iter();
                                    let has_iter = has_iter | i;
                                    <_ as
                                            ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                    while true {
                                        let stmts =
                                            match stmts.next() {
                                                Some(_x) => ::quote::__private::RepInterp(_x),
                                                None => break,
                                            };
                                        ::quote::ToTokens::to_tokens(&stmts, &mut _s);
                                    }
                                }
                                _s
                            });
                        _s
                    });
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Parenthesis,
                    ::quote::__private::TokenStream::new());
                ::quote::__private::push_semi(&mut _s);
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "core");
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "mem");
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "forget");
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Parenthesis,
                    {
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::push_ident(&mut _s, "__guard");
                        _s
                    });
                ::quote::__private::push_semi(&mut _s);
                ::quote::__private::push_ident(&mut _s, "__result");
                _s
            });
        _s
    })parse_quote!({
128        let __guard = ::noexcept::__private::AbortOnDrop;
129        let __result = (move || #ret {
130            #move_self
131            #(
132                let #arg_pat = #arg_val;
133            )*
134            #(#stmts)*
135        })();
136        ::core::mem::forget(__guard);
137        __result
138    });
139
140    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&function, &mut _s);
    _s
}quote!(#function)
141}