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
11#[derive(Default)]
13struct Container {
14 bound: Option<WhereClause>,
16 clone: bool,
18}
19
20enum Strategy {
22 Deep,
24 Clone,
26 With(Path),
28 Default,
30}
31
32fn bind_fields(fields: &Fields) -> TokenStream2 {
34 match fields {
35 Fields::Named(named) => {
36 let bindings = named.named.iter().map(|field| {
37 let name = field.ident.as_ref().expect("named field has an identifier");
38 let binding = binding_ident("e!(#name));
39 quote!(#name: #binding)
40 });
41 quote!({ #(#bindings,)* })
42 }
43 Fields::Unnamed(unnamed) => {
44 let bindings = (0..unnamed.unnamed.len()).map(|index| {
45 let index = Index::from(index);
46 binding_ident("e!(#index))
47 });
48 quote!((#(#bindings,)*))
49 }
50 Fields::Unit => quote!(),
51 }
52}
53
54fn binding_ident(member: &TokenStream2) -> proc_macro2::Ident {
56 format_ident!("field_{}", member.to_string().replace(['.', ' '], "_"))
57}
58
59fn clone_fields(
62 ctor: &TokenStream2,
63 fields: &Fields,
64 access: &dyn Fn(TokenStream2) -> TokenStream2,
65) -> syn::Result<TokenStream2> {
66 Ok(match fields {
67 Fields::Named(named) => {
68 let values = named
69 .named
70 .iter()
71 .map(|field| {
72 let name = field.ident.as_ref().expect("named field has an identifier");
73 let value = field_expr(field, access(quote!(#name)))?;
74 Ok(quote!(#name: #value))
75 })
76 .collect::<syn::Result<Vec<_>>>()?;
77 quote!(#ctor { #(#values,)* })
78 }
79 Fields::Unnamed(unnamed) => {
80 let values = unnamed
81 .unnamed
82 .iter()
83 .enumerate()
84 .map(|(index, field)| {
85 let index = Index::from(index);
86 field_expr(field, access(quote!(#index)))
87 })
88 .collect::<syn::Result<Vec<_>>>()?;
89 quote!(#ctor(#(#values,)*))
90 }
91 Fields::Unit => quote!(#ctor),
92 })
93}
94
95fn container_attrs(input: &DeriveInput) -> syn::Result<Container> {
97 let mut container = Container::default();
98 for attr in input
99 .attrs
100 .iter()
101 .filter(|attr| attr.path().is_ident("deepclone"))
102 {
103 attr.parse_nested_meta(|meta| {
104 if meta.path.is_ident("clone") {
105 container.clone = true;
106 return Ok(());
107 }
108 if !meta.path.is_ident("bound") {
109 return Err(meta.error(
110 "unknown `deepclone` container attribute, expected `clone` or `bound = \"..\"`",
111 ));
112 }
113 let Expr::Lit(syn::ExprLit {
114 lit: syn::Lit::Str(text),
115 ..
116 }) = meta.value()?.parse::<Expr>()?
117 else {
118 return Err(meta.error("`bound` expects a string, as in `bound = \"T: Copy\"`"));
119 };
120 let predicates = text.parse_with(Punctuated::parse_terminated)?;
121 container.bound = Some(WhereClause {
122 where_token: Default::default(),
123 predicates,
124 });
125 Ok(())
126 })?;
127 }
128 Ok(container)
129}
130
131#[proc_macro_derive(DeepClone, attributes(deepclone))]
160pub fn derive_deep_clone(input: TokenStream) -> TokenStream {
161 let input = parse_macro_input!(input as DeriveInput);
162 expand(&input)
163 .unwrap_or_else(syn::Error::into_compile_error)
164 .into()
165}
166
167fn expand(input: &DeriveInput) -> syn::Result<TokenStream2> {
169 let container = container_attrs(input)?;
170 if container.clone {
171 if let Some(field) = fields_of(&input.data)
173 .into_iter()
174 .find(|field| field.attrs.iter().any(|a| a.path().is_ident("deepclone")))
175 {
176 return Err(syn::Error::new(
177 field.span(),
178 "`#[deepclone(clone)]` on the type already clones every field, so a field \
179 attribute here would have no effect",
180 ));
181 }
182 }
183 let body = if container.clone {
186 quote!(::core::clone::Clone::clone(self))
187 } else {
188 match &input.data {
189 Data::Struct(data) => {
190 clone_fields("e!(Self), &data.fields, &|member| quote!(&self.#member))?
191 }
192 Data::Enum(data) => {
193 let arms = data
194 .variants
195 .iter()
196 .map(|variant| {
197 let name = &variant.ident;
198 let bindings = bind_fields(&variant.fields);
199 let fields =
200 clone_fields("e!(Self::#name), &variant.fields, &|member| {
201 let binding = binding_ident(&member);
202 quote!(#binding)
203 })?;
204 Ok(quote!(Self::#name #bindings => #fields))
205 })
206 .collect::<syn::Result<Vec<_>>>()?;
207 quote!(match self { #(#arms,)* })
209 }
210 Data::Union(data) => {
211 return Err(syn::Error::new(
212 data.union_token.span(),
213 "`DeepClone` cannot be derived for unions, because which field is live is \
214 not known statically. `#[deepclone(clone)]` on the type clones it \
215 shallowly instead",
216 ));
217 }
218 }
219 };
220
221 let name = &input.ident;
222 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
223 let where_clause = match container.bound {
224 Some(bound) => bound,
225 None => {
226 let mut clause = where_clause.cloned().unwrap_or_else(|| parse_quote!(where));
227 for param in input.generics.type_params() {
228 let param = ¶m.ident;
229 clause.predicates.push(if container.clone {
231 parse_quote!(#param: ::core::clone::Clone)
232 } else {
233 parse_quote!(#param: ::deepclone::DeepClone)
234 });
235 }
236 clause
237 }
238 };
239
240 Ok(quote! {
241 #[automatically_derived]
242 impl #impl_generics ::deepclone::DeepClone for #name #ty_generics #where_clause {
243 fn deep_clone_in(&self, cloner: &mut ::deepclone::Cloner) -> Self {
244 #body
245 }
246 }
247 })
248}
249
250fn field_expr(field: &syn::Field, access: TokenStream2) -> syn::Result<TokenStream2> {
252 let span = field.ty.span();
254 Ok(match field_strategy(field)? {
255 Strategy::Deep => {
256 quote_spanned!(span => ::deepclone::DeepClone::deep_clone_in(#access, cloner))
257 }
258 Strategy::Clone => quote_spanned!(span => ::core::clone::Clone::clone(#access)),
259 Strategy::With(path) => quote_spanned!(span => #path(#access, cloner)),
260 Strategy::Default => quote_spanned!(span => ::core::default::Default::default()),
261 })
262}
263
264fn field_strategy(field: &syn::Field) -> syn::Result<Strategy> {
266 let mut strategy = None;
267 for attr in field
268 .attrs
269 .iter()
270 .filter(|attr| attr.path().is_ident("deepclone"))
271 {
272 attr.parse_nested_meta(|meta| {
273 let found = if meta.path.is_ident("clone") {
274 Strategy::Clone
275 } else if meta.path.is_ident("default") {
276 Strategy::Default
277 } else if meta.path.is_ident("with") {
278 Strategy::With(meta.value()?.parse()?)
279 } else {
280 return Err(meta.error(
281 "unknown `deepclone` field attribute, expected `clone`, `default`, or `with`",
282 ));
283 };
284 if strategy.is_some() {
285 return Err(meta.error("conflicting `deepclone` field attributes"));
286 }
287 strategy = Some(found);
288 Ok(())
289 })?;
290 }
291 Ok(strategy.unwrap_or(Strategy::Deep))
292}
293
294fn fields_of(data: &Data) -> Vec<&syn::Field> {
296 match data {
297 Data::Struct(data) => data.fields.iter().collect(),
298 Data::Enum(data) => data
299 .variants
300 .iter()
301 .flat_map(|variant| variant.fields.iter())
302 .collect(),
303 Data::Union(data) => data.fields.named.iter().collect(),
304 }
305}