evmap_derive/lib.rs
1//! This crate provides procedural derive macros to simplify the usage of `evmap`.
2//!
3//! Currently, only `#[derive(ShallowCopy)]` is supported; see below.
4#![warn(missing_docs, rust_2018_idioms, broken_intra_doc_links)]
5
6#[allow(unused_extern_crates)]
7extern crate proc_macro;
8
9use proc_macro2::{Ident, TokenStream};
10use quote::{format_ident, quote, quote_spanned};
11use syn::spanned::Spanned;
12use syn::{
13 parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Generics, Index,
14};
15
16/// Implementation for `#[derive(ShallowCopy)]`
17///
18/// evmap provides the [`ShallowCopy`](evmap::shallow_copy::ShallowCopy) trait, which allows you to
19/// cheaply alias types that don't otherwise implement `Copy`. Basic implementations are provided
20/// for common types such as `String` and `Vec`, but it must be implemented manually for structs
21/// using these types.
22///
23/// This macro attempts to simplify this task. It only works on types whose members all implement
24/// `ShallowCopy`. If this is not possible, consider using
25/// [`CopyValue`](evmap::shallow_copy::CopyValue), `Box`, or `Arc` instead.
26///
27/// ## Usage example
28/// ```
29/// # use evmap_derive::ShallowCopy;
30/// #[derive(ShallowCopy)]
31/// struct Thing { field: i32 }
32///
33/// #[derive(ShallowCopy)]
34/// struct Generic<T> { field: T }
35///
36/// #[derive(ShallowCopy)]
37/// enum Things<T> { One(Thing), Two(Generic<T>) }
38/// ```
39///
40/// ## Generated implementations
41/// The generated implementation calls
42/// [`shallow_copy`](evmap::shallow_copy::ShallowCopy::shallow_copy) on all the members of the
43/// type, and lifts the `ManuallyDrop` wrappers to the top-level return type.
44///
45/// For generic types, the derive adds `ShallowCopy` bounds to all the type parameters.
46///
47/// For instance, for the following code...
48/// ```
49/// # use evmap_derive::ShallowCopy;
50/// #[derive(ShallowCopy)]
51/// struct Generic<T> { field: T }
52/// ```
53/// ...the derive generates...
54/// ```
55/// # use evmap::shallow_copy::ShallowCopy;
56/// # use std::mem::ManuallyDrop;
57/// # struct Generic<T> { field: T }
58/// impl<T: ShallowCopy> ShallowCopy for Generic<T> {
59/// unsafe fn shallow_copy(&self) -> ManuallyDrop<Self> {
60/// ManuallyDrop::new(Self {
61/// field: ManuallyDrop::into_inner(ShallowCopy::shallow_copy(&self.field))
62/// })
63/// }
64/// }
65/// ```
66#[proc_macro_derive(ShallowCopy)]
67pub fn derive_shallow_copy(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
68 let input = parse_macro_input!(input as DeriveInput);
69 let name = input.ident;
70 let generics = add_trait_bounds(input.generics);
71 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
72 let copy = fields(&input.data, &name);
73 proc_macro::TokenStream::from(quote! {
74 impl #impl_generics evmap::shallow_copy::ShallowCopy for #name #ty_generics #where_clause {
75 unsafe fn shallow_copy(&self) -> std::mem::ManuallyDrop<Self> {
76 #copy
77 }
78 }
79 })
80}
81
82fn add_trait_bounds(mut generics: Generics) -> Generics {
83 for param in &mut generics.params {
84 if let GenericParam::Type(ref mut type_param) = *param {
85 type_param
86 .bounds
87 .push(parse_quote!(evmap::shallow_copy::ShallowCopy));
88 }
89 }
90 generics
91}
92
93fn fields(data: &Data, type_name: &Ident) -> TokenStream {
94 match data {
95 Data::Struct(data) => match &data.fields {
96 Fields::Named(fields) => {
97 let recurse = fields.named.iter().map(|f| {
98 let name = &f.ident;
99 quote_spanned! {f.span()=>
100 #name: std::mem::ManuallyDrop::into_inner(
101 evmap::shallow_copy::ShallowCopy::shallow_copy(&self.#name)
102 )
103 }
104 });
105 quote! {
106 std::mem::ManuallyDrop::new(Self { #(#recurse,)* })
107 }
108 }
109 Fields::Unnamed(fields) => {
110 let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
111 let index = Index::from(i);
112 quote_spanned! {f.span()=>
113 std::mem::ManuallyDrop::into_inner(
114 evmap::shallow_copy::ShallowCopy::shallow_copy(&self.#index)
115 )
116 }
117 });
118 quote! {
119 std::mem::ManuallyDrop::new(#type_name(#(#recurse,)*))
120 }
121 }
122 Fields::Unit => quote!(std::mem::ManuallyDrop::new(#type_name)),
123 },
124 Data::Enum(data) => {
125 let recurse = data.variants.iter().map(|f| {
126 let (names, fields) = match &f.fields {
127 Fields::Named(fields) => {
128 let field_names = f.fields.iter().map(|field| {
129 let ident = field.ident.as_ref().unwrap();
130 quote_spanned! {
131 field.span()=> #ident
132 }
133 });
134 let recurse = fields.named.iter().map(|f| {
135 let name = f.ident.as_ref().unwrap();
136 quote_spanned! {f.span()=>
137 #name: std::mem::ManuallyDrop::into_inner(
138 evmap::shallow_copy::ShallowCopy::shallow_copy(#name)
139 )
140 }
141 });
142 (quote! { {#(#field_names,)*} }, quote! { { #(#recurse,)* } })
143 }
144 Fields::Unnamed(fields) => {
145 let field_names = f.fields.iter().enumerate().map(|(i, field)| {
146 let ident = format_ident!("x{}", i);
147 quote_spanned! {
148 field.span()=> #ident
149 }
150 });
151 let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
152 let ident = format_ident!("x{}", i);
153 quote_spanned! {f.span()=>
154 std::mem::ManuallyDrop::into_inner(
155 evmap::shallow_copy::ShallowCopy::shallow_copy(#ident)
156 )
157 }
158 });
159 (quote! { (#(#field_names,)*) }, quote! { (#(#recurse,)*) })
160 }
161 Fields::Unit => (quote!(), quote!()),
162 };
163 let name = &f.ident;
164 quote_spanned! {f.span()=>
165 #type_name::#name#names => std::mem::ManuallyDrop::new(#type_name::#name#fields)
166 }
167 });
168 quote! {
169 match self {
170 #(#recurse,)*
171 }
172 }
173 }
174 Data::Union(_) => unimplemented!("Unions are not supported"),
175 }
176}