no_panic/
lib.rs

1//! [![github]](https://github.com/dtolnay/no-panic) [![crates-io]](https://crates.io/crates/no-panic) [![docs-rs]](https://docs.rs/no-panic)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! A Rust attribute macro to require that the compiler prove a function can't
10//! ever panic.
11//!
12//! ```toml
13//! [dependencies]
14//! no-panic = "0.1"
15//! ```
16//!
17//! ```
18//! use no_panic::no_panic;
19//!
20//! #[no_panic]
21//! fn demo(s: &str) -> &str {
22//!     &s[1..]
23//! }
24//!
25//! fn main() {
26//!     # fn demo(s: &str) -> &str {
27//!     #     &s[1..]
28//!     # }
29//!     #
30//!     println!("{}", demo("input string"));
31//! }
32//! ```
33//!
34//! If the function does panic (or the compiler fails to prove that the function
35//! cannot panic), the program fails to compile with a linker error that
36//! identifies the function name. Let's trigger that by passing a string that
37//! cannot be sliced at the first byte:
38//!
39//! ```should_panic
40//! # fn demo(s: &str) -> &str {
41//! #     &s[1..]
42//! # }
43//! #
44//! fn main() {
45//!     println!("{}", demo("\u{1f980}input string"));
46//! }
47//! ```
48//!
49//! ```console
50//!    Compiling no-panic-demo v0.0.1
51//! error: linking with `cc` failed: exit code: 1
52//!   |
53//!   = note: /no-panic-demo/target/release/deps/no_panic_demo-7170785b672ae322.no_p
54//! anic_demo1-cba7f4b666ccdbcbbf02b7348e5df1b2.rs.rcgu.o: In function `_$LT$no_pani
55//! c_demo..demo..__NoPanic$u20$as$u20$core..ops..drop..Drop$GT$::drop::h72f8f423002
56//! b8d9f':
57//!           no_panic_demo1-cba7f4b666ccdbcbbf02b7348e5df1b2.rs:(.text._ZN72_$LT$no
58//! _panic_demo..demo..__NoPanic$u20$as$u20$core..ops..drop..Drop$GT$4drop17h72f8f42
59//! 3002b8d9fE+0x2): undefined reference to `
60//!
61//!           ERROR[no-panic]: detected panic in function `demo`
62//!           '
63//!           collect2: error: ld returned 1 exit status
64//! ```
65//!
66//! The error is not stellar but notice the ERROR\[no-panic\] part at the end
67//! that provides the name of the offending function.
68//!
69//! <br>
70//!
71//! ## Caveats
72//!
73//! - Functions that require some amount of optimization to prove that they do
74//!   not panic may no longer compile in debug mode after being marked
75//!   `#[no_panic]`.
76//!
77//! - Panic detection happens at link time across the entire dependency graph,
78//!   so any Cargo commands that do not invoke a linker will not trigger panic
79//!   detection. This includes `cargo build` of library crates and `cargo check`
80//!   of binary and library crates.
81//!
82//! - The attribute is useless in code built with `panic = "abort"`. Code must
83//!   be built with `panic = "unwind"` (the default) in order for any panics to
84//!   be detected. After confirming absence of panics, you can of course still
85//!   ship your software as a `panic = "abort"` build.
86//!
87//! - Const functions are not supported. The attribute will fail to compile if
88//!   placed on a `const fn`.
89//!
90//! If you find that code requires optimization to pass `#[no_panic]`, either
91//! make no-panic an optional dependency that you only enable in release builds,
92//! or add a section like the following to your Cargo.toml or .cargo/config.toml
93//! to enable very basic optimization in debug builds.
94//!
95//! ```toml
96//! [profile.dev]
97//! opt-level = 1
98//! ```
99//!
100//! If the code that you need to prove isn't panicking makes function calls to
101//! non-generic non-inline functions from a different crate, you may need thin
102//! LTO enabled for the linker to deduce those do not panic.
103//!
104//! ```toml
105//! [profile.release]
106//! lto = "thin"
107//! ```
108//!
109//! If thin LTO isn't cutting it, the next thing to try would be fat LTO with a
110//! single codegen unit:
111//!
112//! ```toml
113//! [profile.release]
114//! lto = "fat"
115//! codegen-units = 1
116//! ```
117//!
118//! If you want no_panic to just assume that some function you call doesn't
119//! panic, and get Undefined Behavior if it does at runtime, see
120//! [dtolnay/no-panic#16]; try wrapping that call in an `unsafe extern "C"`
121//! wrapper.
122//!
123//! [dtolnay/no-panic#16]: https://github.com/dtolnay/no-panic/issues/16
124//!
125//! <br>
126//!
127//! ## Acknowledgments
128//!
129//! The linker error technique is based on [Kixunil]'s crate [`dont_panic`].
130//! Check out that crate for other convenient ways to require absence of panics.
131//!
132//! [Kixunil]: https://github.com/Kixunil
133//! [`dont_panic`]: https://github.com/Kixunil/dont_panic
134
135#![doc(html_root_url = "https://docs.rs/no-panic/0.1.36")]
136#![allow(
137    clippy::doc_markdown,
138    clippy::match_same_arms,
139    clippy::missing_panics_doc,
140    clippy::uninlined_format_args
141)]
142#![cfg_attr(all(test, exhaustive), feature(non_exhaustive_omitted_patterns_lint))]
143
144extern crate proc_macro;
145
146use proc_macro::TokenStream;
147use proc_macro2::{Span, TokenStream as TokenStream2};
148use quote::quote;
149use std::mem;
150use syn::parse::{Error, Nothing, Result};
151use syn::{
152    parse_quote, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path, PathArguments,
153    ReturnType, Token, Type, TypeInfer, TypeParamBound,
154};
155
156#[proc_macro_attribute]
157pub fn no_panic(args: TokenStream, input: TokenStream) -> TokenStream {
158    let args = TokenStream2::from(args);
159    let input = TokenStream2::from(input);
160    TokenStream::from(match parse(args, input.clone()) {
161        Ok(function) => {
162            let expanded = expand_no_panic(function);
163            {
    let mut _s = ::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, "cfg");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "not");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "doc");
                            _s
                        });
                    _s
                });
            _s
        });
    ::quote::ToTokens::to_tokens(&expanded, &mut _s);
    ::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, "cfg");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "doc");
                    _s
                });
            _s
        });
    ::quote::ToTokens::to_tokens(&input, &mut _s);
    _s
}quote! {
164                #[cfg(not(doc))]
165                #expanded
166                // Keep generated parameter names out of doc builds.
167                #[cfg(doc)]
168                #input
169            }
170        }
171        Err(parse_error) => {
172            let compile_error = parse_error.to_compile_error();
173            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&compile_error, &mut _s);
    ::quote::ToTokens::to_tokens(&input, &mut _s);
    _s
}quote! {
174                #compile_error
175                #input
176            }
177        }
178    })
179}
180
181fn parse(args: TokenStream2, input: TokenStream2) -> Result<ItemFn> {
182    let function: ItemFn = syn::parse2(input)?;
183    let _: Nothing = syn::parse2::<Nothing>(args)?;
184    if function.sig.constness.is_some() {
185        return Err(Error::new(
186            Span::call_site(),
187            "no_panic attribute on const fn is not supported",
188        ));
189    }
190    if function.sig.asyncness.is_some() {
191        return Err(Error::new(
192            Span::call_site(),
193            "no_panic attribute on async fn is not supported",
194        ));
195    }
196    Ok(function)
197}
198
199// Convert `Path<impl Trait>` to `Path<_>`
200fn make_impl_trait_wild(ret: &mut Type) {
201    match ret {
202        #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
203        Type::ImplTrait(impl_trait) => {
204            *ret = Type::Infer(TypeInfer {
205                underscore_token: ::syn::token::UnderscoreToken![_](impl_trait.impl_token.span),
206            });
207        }
208        Type::Array(ret) => make_impl_trait_wild(&mut ret.elem),
209        Type::Group(ret) => make_impl_trait_wild(&mut ret.elem),
210        Type::Paren(ret) => make_impl_trait_wild(&mut ret.elem),
211        Type::Path(ret) => make_impl_trait_wild_in_path(&mut ret.path),
212        Type::Ptr(ret) => make_impl_trait_wild(&mut ret.elem),
213        Type::Reference(ret) => make_impl_trait_wild(&mut ret.elem),
214        Type::Slice(ret) => make_impl_trait_wild(&mut ret.elem),
215        Type::TraitObject(ret) => {
216            for bound in &mut ret.bounds {
217                if let TypeParamBound::Trait(bound) = bound {
218                    make_impl_trait_wild_in_path(&mut bound.path);
219                }
220            }
221        }
222        Type::Tuple(ret) => ret.elems.iter_mut().for_each(make_impl_trait_wild),
223        Type::BareFn(_) | Type::Infer(_) | Type::Macro(_) | Type::Never(_) | Type::Verbatim(_) => {}
224        _ => {}
225    }
226}
227
228fn make_impl_trait_wild_in_path(path: &mut Path) {
229    for segment in &mut path.segments {
230        if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments {
231            for arg in &mut bracketed.args {
232                if let GenericArgument::Type(arg) = arg {
233                    make_impl_trait_wild(arg);
234                }
235            }
236        }
237    }
238}
239
240fn expand_no_panic(mut function: ItemFn) -> TokenStream2 {
241    let mut move_self = None;
242    let mut arg_attrs = Vec::new();
243    let mut arg_pat = Vec::new();
244    let mut arg_val = Vec::new();
245    for (i, input) in function.sig.inputs.iter_mut().enumerate() {
246        match input {
247            FnArg::Typed(PatType { attrs, pat, .. })
248                if match pat.as_ref() {
249                    Pat::Ident(pat) => pat.ident != "self",
250                    _ => true,
251                } =>
252            {
253                let arg_name = if let Pat::Ident(original_name) = &**pat {
254                    original_name.ident.clone()
255                } else {
256                    Ident::new(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("__arg{0}", i))
    })format!("__arg{}", i), Span::call_site())
257                };
258                arg_attrs.push(attrs);
259                arg_pat.push(mem::replace(&mut *pat, ::syn::__private::parse_quote({
        let mut _s = ::quote::__private::TokenStream::new();
        ::quote::__private::push_ident(&mut _s, "mut");
        ::quote::ToTokens::to_tokens(&arg_name, &mut _s);
        _s
    })parse_quote!(mut #arg_name)));
260                arg_val.push(arg_name);
261            }
262            FnArg::Typed(_) | FnArg::Receiver(_) => {
263                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! {
264                    if false {
265                        loop {}
266                        #[allow(unreachable_code)]
267                        {
268                            let __self = self;
269                        }
270                    }
271                });
272            }
273        }
274    }
275
276    let has_inline = function
277        .attrs
278        .iter()
279        .any(|attr| attr.path().is_ident("inline"));
280    if !has_inline {
281        function.attrs.push(::syn::__private::parse_quote({
        let mut _s = ::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, "inline");
                _s
            });
        _s
    })parse_quote!(#[inline]));
282    }
283
284    let ret = match &function.sig.output {
285        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!(-> ()),
286        ReturnType::Type(arrow, output) => {
287            let mut output = output.clone();
288            make_impl_trait_wild(&mut output);
289            {
    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)
290        }
291    };
292    let stmts = function.block.stmts;
293    let message = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("\n\nERROR[no-panic]: detected panic in function `{0}`\n",
                function.sig.ident))
    })format!(
294        "\n\nERROR[no-panic]: detected panic in function `{}`\n",
295        function.sig.ident,
296    );
297    let unsafe_extern = if falsecfg!(no_unsafe_extern_blocks) {
298        None
299    } else {
300        Some(::syn::token::UnsafeToken![unsafe](Span::call_site()))
301    };
302    *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, "struct");
                ::quote::__private::push_ident(&mut _s, "__NoPanic");
                ::quote::__private::push_semi(&mut _s);
                ::quote::ToTokens::to_tokens(&unsafe_extern, &mut _s);
                ::quote::__private::push_ident(&mut _s, "extern");
                ::quote::__private::parse(&mut _s, "\"C\"");
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Brace,
                    {
                        let mut _s = ::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, "link_name");
                                ::quote::__private::push_eq(&mut _s);
                                ::quote::ToTokens::to_tokens(&message, &mut _s);
                                _s
                            });
                        ::quote::__private::push_ident(&mut _s, "fn");
                        ::quote::__private::push_ident(&mut _s, "trigger");
                        ::quote::__private::push_group(&mut _s,
                            ::quote::__private::Delimiter::Parenthesis,
                            ::quote::__private::TokenStream::new());
                        ::quote::__private::push_rarrow(&mut _s);
                        ::quote::__private::push_bang(&mut _s);
                        ::quote::__private::push_semi(&mut _s);
                        _s
                    });
                ::quote::__private::push_ident(&mut _s, "impl");
                ::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, "ops");
                ::quote::__private::push_colon2(&mut _s);
                ::quote::__private::push_ident(&mut _s, "Drop");
                ::quote::__private::push_ident(&mut _s, "for");
                ::quote::__private::push_ident(&mut _s, "__NoPanic");
                ::quote::__private::push_group(&mut _s,
                    ::quote::__private::Delimiter::Brace,
                    {
                        let mut _s = ::quote::__private::TokenStream::new();
                        ::quote::__private::push_ident(&mut _s, "fn");
                        ::quote::__private::push_ident(&mut _s, "drop");
                        ::quote::__private::push_group(&mut _s,
                            ::quote::__private::Delimiter::Parenthesis,
                            {
                                let mut _s = ::quote::__private::TokenStream::new();
                                ::quote::__private::push_and(&mut _s);
                                ::quote::__private::push_ident(&mut _s, "mut");
                                ::quote::__private::push_ident(&mut _s, "self");
                                _s
                            });
                        ::quote::__private::push_group(&mut _s,
                            ::quote::__private::Delimiter::Brace,
                            {
                                let mut _s = ::quote::__private::TokenStream::new();
                                ::quote::__private::push_ident(&mut _s, "unsafe");
                                ::quote::__private::push_group(&mut _s,
                                    ::quote::__private::Delimiter::Brace,
                                    {
                                        let mut _s = ::quote::__private::TokenStream::new();
                                        ::quote::__private::push_ident(&mut _s, "trigger");
                                        ::quote::__private::push_group(&mut _s,
                                            ::quote::__private::Delimiter::Parenthesis,
                                            ::quote::__private::TokenStream::new());
                                        ::quote::__private::push_semi(&mut _s);
                                        _s
                                    });
                                _s
                            });
                        _s
                    });
                ::quote::__private::push_ident(&mut _s, "let");
                ::quote::__private::push_ident(&mut _s, "__guard");
                ::quote::__private::push_eq(&mut _s);
                ::quote::__private::push_ident(&mut _s, "__NoPanic");
                ::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_attrs, i) = arg_attrs.quote_into_iter();
                                    let has_iter = has_iter | i;
                                    #[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_attrs =
                                            match arg_attrs.next() {
                                                Some(_x) => ::quote::__private::RepInterp(_x),
                                                None => break,
                                            };
                                        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,
                                            };
                                        {
                                            use ::quote::__private::ext::*;
                                            let has_iter = ::quote::__private::HasIterator::<false>;
                                            #[allow(unused_mut)]
                                            let (mut arg_attrs, i) = arg_attrs.quote_into_iter();
                                            let has_iter = has_iter | i;
                                            <_ as
                                                    ::quote::__private::CheckHasIterator<true>>::check(has_iter);
                                            while true {
                                                let arg_attrs =
                                                    match arg_attrs.next() {
                                                        Some(_x) => ::quote::__private::RepInterp(_x),
                                                        None => break,
                                                    };
                                                ::quote::ToTokens::to_tokens(&arg_attrs, &mut _s);
                                            }
                                        }
                                        ::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!({
303        struct __NoPanic;
304        #unsafe_extern extern "C" {
305            #[link_name = #message]
306            fn trigger() -> !;
307        }
308        impl ::core::ops::Drop for __NoPanic {
309            fn drop(&mut self) {
310                unsafe {
311                    trigger();
312                }
313            }
314        }
315        let __guard = __NoPanic;
316        let __result = (move || #ret {
317            #move_self
318            #(
319                #(#arg_attrs)*
320                let #arg_pat = #arg_val;
321            )*
322            #(#stmts)*
323        })();
324        ::core::mem::forget(__guard);
325        __result
326    });
327
328    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&function, &mut _s);
    _s
}quote!(#function)
329}