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
#![no_std]

use quote::quote;
use syn::parse_macro_input;
use syn::Token;

#[proc_macro]
pub fn defr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    parse_macro_input!(input as Tts).0.into()
}

struct Tts(proc_macro2::TokenStream);

impl syn::parse::Parse for Tts {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let exprs = syn::punctuated::Punctuated::<syn::Expr, Token![;]>::parse_terminated(input)?
            .into_iter();
        let b = proc_macro2::Ident::new("__body", proc_macro2::Span::mixed_site());
        let d = proc_macro2::Ident::new("__defr", proc_macro2::Span::mixed_site());
        let tts = quote! {
            let #b = || { #(#exprs;)* };
            let #d = {
                #[doc(hidden)]
                struct __Defr<F: std::ops::FnOnce()>(std::option::Option<F>);
                impl<F: std::ops::FnOnce()> std::ops::Drop for __Defr<F> {
                    fn drop(&mut self) {
                        self.0.take().unwrap()();
                    }
                }
                __Defr(std::option::Option::Some(#b))
            };
        };
        Ok(Tts(tts))
    }
}