destruct_drop_derive/
lib.rs1use 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}