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
45fn 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,
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}