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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use proc_macro::{self, TokenStream};
use quote::quote;
use syn::{
    parse_macro_input, spanned::Spanned, DataEnum, DeriveInput, FieldsNamed, FieldsUnnamed, Ident,
    Index, Member,
};

#[proc_macro_derive(DestructDrop)]
pub fn derive_destruct_drop(input: TokenStream) -> TokenStream {
    let DeriveInput { ident, data, .. } = parse_macro_input!(input);

    let drop_code = match data {
        syn::Data::Struct(s) => {
            let members = match s.fields {
                syn::Fields::Named(FieldsNamed { named, .. }) => named
                    .into_iter()
                    .map(|f| Member::Named(f.ident.unwrap()))
                    .collect(),
                syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed
                    .into_iter()
                    .enumerate()
                    .map(|(i, f)| {
                        Member::Unnamed(Index {
                            index: i as u32,
                            span: f.span(),
                        })
                    })
                    .collect(),
                syn::Fields::Unit => vec![],
            };

            quote! {
                #( unsafe { ::core::ptr::drop_in_place(&mut this.#members); } )*
            }
        }
        syn::Data::Enum(DataEnum { variants, .. }) => {
            let variant_match_code = variants.into_iter().map(|v| {
                let v_ident = v.ident;
                match v.fields {
                    syn::Fields::Named(FieldsNamed { named, .. }) => {
                        let fields = named.into_iter()
                            .map(|f| f.ident.unwrap())
                            .collect::<Vec<_>>();
                        quote! {
                            #ident::#v_ident {#( ref mut #fields)*,} => { #( unsafe { ::core::ptr::drop_in_place(#fields); } )* }
                        }
                    }
                    syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
                        let fields = unnamed.into_iter()
                            .enumerate()
                            .map(|(i, f)| Ident::new(format!("__f_{}", i).as_str(), f.span()))
                            .collect::<Vec<_>>();
                        quote! {
                            #ident::#v_ident (#( ref mut #fields)*,) => { #( unsafe { ::core::ptr::drop_in_place(#fields); } )* }
                        }
                    }
                    syn::Fields::Unit => quote! {
                        #ident::#v_ident => {}
                    },
                }
            });
            quote! {
                match *this {
                    #( #variant_match_code )*,
                }
            }
        }
        syn::Data::Union(_) => {
            panic!("DestructDrop cannot be derived for unions.");
        }
    };

    (quote! {
        impl #ident {
            fn destruct_drop(self) {
                let mut this = ::core::mem::ManuallyDrop::new(self);
                #drop_code
            }
        }
    }).into()
}