destruct_drop_derive/
lib.rs

1use proc_macro::{self, TokenStream};
2use quote::quote;
3use syn::{
4    parse_macro_input, spanned::Spanned, DataEnum, DeriveInput, FieldsNamed, FieldsUnnamed, Ident,
5    Index, Member,
6};
7
8#[proc_macro_derive(DestructDrop)]
9pub fn derive_destruct_drop(input: TokenStream) -> TokenStream {
10    let DeriveInput { ident, data, generics, .. } = parse_macro_input!(input);
11
12    let drop_code = match data {
13        syn::Data::Struct(s) => {
14            let members = match s.fields {
15                syn::Fields::Named(FieldsNamed { named, .. }) => named
16                    .into_iter()
17                    .map(|f| Member::Named(f.ident.unwrap()))
18                    .collect(),
19                syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed
20                    .into_iter()
21                    .enumerate()
22                    .map(|(i, f)| {
23                        Member::Unnamed(Index {
24                            index: i as u32,
25                            span: f.span(),
26                        })
27                    })
28                    .collect(),
29                syn::Fields::Unit => vec![],
30            };
31
32            quote! {
33                #( unsafe { ::core::ptr::drop_in_place(&mut this.#members); } )*
34            }
35        }
36        syn::Data::Enum(DataEnum { variants, .. }) => {
37            let variant_match_code = variants.into_iter().map(|v| {
38                let v_ident = v.ident;
39                match v.fields {
40                    syn::Fields::Named(FieldsNamed { named, .. }) => {
41                        let fields = named.into_iter()
42                            .map(|f| f.ident.unwrap())
43                            .collect::<Vec<_>>();
44                        quote! {
45                            #ident::#v_ident {#( ref mut #fields)*,} => { #( unsafe { ::core::ptr::drop_in_place(#fields); } )* }
46                        }
47                    }
48                    syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
49                        let fields = unnamed.into_iter()
50                            .enumerate()
51                            .map(|(i, f)| Ident::new(format!("__f_{}", i).as_str(), f.span()))
52                            .collect::<Vec<_>>();
53                        quote! {
54                            #ident::#v_ident (#( ref mut #fields)*,) => { #( unsafe { ::core::ptr::drop_in_place(#fields); } )* }
55                        }
56                    }
57                    syn::Fields::Unit => quote! {
58                        #ident::#v_ident => {}
59                    },
60                }
61            });
62            quote! {
63                match *this {
64                    #( #variant_match_code )*,
65                }
66            }
67        }
68        syn::Data::Union(_) => {
69            panic!("DestructDrop cannot be derived for unions.");
70        }
71    };
72
73    (quote! {
74        impl #generics ::destruct_drop::DestructDrop for #ident #generics {
75            fn destruct_drop(self) {
76                let mut this = ::core::mem::ManuallyDrop::new(self);
77                #drop_code
78            }
79        }
80    }).into()
81}