1use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::{format_ident, quote, quote_spanned};
6use syn::{
7 Data, DeriveInput, Expr, Fields, Index, Path, WhereClause, parse_macro_input, parse_quote,
8 punctuated::Punctuated, spanned::Spanned,
9};
10
11enum Strategy {
13 Deep,
15 Clone,
17 With(Path),
19 Default,
21}
22
23fn bind_fields(fields: &Fields) -> TokenStream2 {
25 match fields {
26 Fields::Named(named) => {
27 let bindings = named.named.iter().map(|field| {
28 let name = field.ident.as_ref().expect("named field has an identifier");
29 let binding = binding_ident("e!(#name));
30 quote!(#name: #binding)
31 });
32 quote!({ #(#bindings,)* })
33 }
34 Fields::Unnamed(unnamed) => {
35 let bindings = (0..unnamed.unnamed.len()).map(|index| {
36 let index = Index::from(index);
37 binding_ident("e!(#index))
38 });
39 quote!((#(#bindings,)*))
40 }
41 Fields::Unit => quote!(),
42 }
43}
44
45fn binding_ident(member: &TokenStream2) -> proc_macro2::Ident {
47 format_ident!("field_{}", member.to_string().replace(['.', ' '], "_"))
48}
49
50fn clone_fields(
53 ctor: &TokenStream2,
54 fields: &Fields,
55 access: &dyn Fn(TokenStream2) -> TokenStream2,
56) -> syn::Result<TokenStream2> {
57 Ok(match fields {
58 Fields::Named(named) => {
59 let values = named
60 .named
61 .iter()
62 .map(|field| {
63 let name = field.ident.as_ref().expect("named field has an identifier");
64 let value = field_expr(field, access(quote!(#name)))?;
65 Ok(quote!(#name: #value))
66 })
67 .collect::<syn::Result<Vec<_>>>()?;
68 quote!(#ctor { #(#values,)* })
69 }
70 Fields::Unnamed(unnamed) => {
71 let values = unnamed
72 .unnamed
73 .iter()
74 .enumerate()
75 .map(|(index, field)| {
76 let index = Index::from(index);
77 field_expr(field, access(quote!(#index)))
78 })
79 .collect::<syn::Result<Vec<_>>>()?;
80 quote!(#ctor(#(#values,)*))
81 }
82 Fields::Unit => quote!(#ctor),
83 })
84}
85
86fn container_bound(input: &DeriveInput) -> syn::Result<Option<WhereClause>> {
88 let mut bound = None;
89 for attr in input
90 .attrs
91 .iter()
92 .filter(|attr| attr.path().is_ident("deepclone"))
93 {
94 attr.parse_nested_meta(|meta| {
95 if !meta.path.is_ident("bound") {
96 return Err(meta
97 .error("unknown `deepclone` container attribute, expected `bound = \"..\"`"));
98 }
99 let Expr::Lit(syn::ExprLit {
100 lit: syn::Lit::Str(text),
101 ..
102 }) = meta.value()?.parse::<Expr>()?
103 else {
104 return Err(meta.error("`bound` expects a string, as in `bound = \"T: Copy\"`"));
105 };
106 let predicates = text.parse_with(Punctuated::parse_terminated)?;
107 bound = Some(WhereClause {
108 where_token: Default::default(),
109 predicates,
110 });
111 Ok(())
112 })?;
113 }
114 Ok(bound)
115}
116
117#[proc_macro_derive(DeepClone, attributes(deepclone))]
142pub fn derive_deep_clone(input: TokenStream) -> TokenStream {
143 let input = parse_macro_input!(input as DeriveInput);
144 expand(&input)
145 .unwrap_or_else(syn::Error::into_compile_error)
146 .into()
147}
148
149fn expand(input: &DeriveInput) -> syn::Result<TokenStream2> {
151 let body = match &input.data {
152 Data::Struct(data) => {
153 clone_fields("e!(Self), &data.fields, &|member| quote!(&self.#member))?
154 }
155 Data::Enum(data) => {
156 let arms = data
157 .variants
158 .iter()
159 .map(|variant| {
160 let name = &variant.ident;
161 let bindings = bind_fields(&variant.fields);
162 let fields = clone_fields("e!(Self::#name), &variant.fields, &|member| {
163 let binding = binding_ident(&member);
164 quote!(#binding)
165 })?;
166 Ok(quote!(Self::#name #bindings => #fields))
167 })
168 .collect::<syn::Result<Vec<_>>>()?;
169 quote!(match self { #(#arms,)* })
171 }
172 Data::Union(data) => {
173 return Err(syn::Error::new(
174 data.union_token.span(),
175 "`DeepClone` cannot be derived for unions, because which field is live is not \
176 known statically",
177 ));
178 }
179 };
180
181 let name = &input.ident;
182 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
183 let where_clause = match container_bound(input)? {
184 Some(bound) => bound,
185 None => {
186 let mut clause = where_clause.cloned().unwrap_or_else(|| parse_quote!(where));
187 for param in input.generics.type_params() {
188 let param = ¶m.ident;
189 clause
190 .predicates
191 .push(parse_quote!(#param: ::deepclone::DeepClone));
192 }
193 clause
194 }
195 };
196
197 Ok(quote! {
198 #[automatically_derived]
199 impl #impl_generics ::deepclone::DeepClone for #name #ty_generics #where_clause {
200 fn deep_clone_in(&self, cloner: &mut ::deepclone::Cloner) -> Self {
201 #body
202 }
203 }
204 })
205}
206
207fn field_expr(field: &syn::Field, access: TokenStream2) -> syn::Result<TokenStream2> {
209 let span = field.ty.span();
211 Ok(match field_strategy(field)? {
212 Strategy::Deep => {
213 quote_spanned!(span => ::deepclone::DeepClone::deep_clone_in(#access, cloner))
214 }
215 Strategy::Clone => quote_spanned!(span => ::core::clone::Clone::clone(#access)),
216 Strategy::With(path) => quote_spanned!(span => #path(#access, cloner)),
217 Strategy::Default => quote_spanned!(span => ::core::default::Default::default()),
218 })
219}
220
221fn field_strategy(field: &syn::Field) -> syn::Result<Strategy> {
223 let mut strategy = None;
224 for attr in field
225 .attrs
226 .iter()
227 .filter(|attr| attr.path().is_ident("deepclone"))
228 {
229 attr.parse_nested_meta(|meta| {
230 let found = if meta.path.is_ident("clone") {
231 Strategy::Clone
232 } else if meta.path.is_ident("default") {
233 Strategy::Default
234 } else if meta.path.is_ident("with") {
235 Strategy::With(meta.value()?.parse()?)
236 } else {
237 return Err(meta.error(
238 "unknown `deepclone` field attribute, expected `clone`, `default`, or `with`",
239 ));
240 };
241 if strategy.is_some() {
242 return Err(meta.error("conflicting `deepclone` field attributes"));
243 }
244 strategy = Some(found);
245 Ok(())
246 })?;
247 }
248 Ok(strategy.unwrap_or(Strategy::Deep))
249}