derivative/
clone.rs

1use proc_macro2;
2
3use ast;
4use attr;
5use matcher;
6use syn;
7use utils;
8
9/// Derive `Copy` for `input`.
10pub fn derive_copy(input: &ast::Input) -> proc_macro2::TokenStream {
11    let name = &input.ident;
12
13    let copy_trait_path = copy_trait_path();
14    let generics = utils::build_impl_generics(
15        input,
16        &copy_trait_path,
17        |attrs| attrs.copy_bound().is_none(),
18        |field| field.copy_bound(),
19        |input| input.copy_bound(),
20    );
21    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
22
23    quote! {
24        #[allow(unused_qualifications)]
25        impl #impl_generics #copy_trait_path for #name #ty_generics #where_clause {}
26    }
27}
28
29/// Derive `Clone` for `input`.
30pub fn derive_clone(input: &ast::Input) -> proc_macro2::TokenStream {
31    let name = &input.ident;
32
33    let clone_trait_path = clone_trait_path();
34    let generics = utils::build_impl_generics(
35        input,
36        &clone_trait_path,
37        needs_clone_bound,
38        |field| field.clone_bound(),
39        |input| input.clone_bound(),
40    );
41    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
42
43    let is_copy = input.attrs.copy.is_some();
44    if is_copy && input.generics.type_params().count() == 0 {
45        quote! {
46            #[allow(unused_qualifications)]
47            impl #impl_generics #clone_trait_path for #name #ty_generics #where_clause {
48                fn clone(&self) -> Self {
49                    *self
50                }
51            }
52        }
53    } else {
54        let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed).build_arms(
55            input,
56            "__arg",
57            |arm_path, _, _, style, _, bis| {
58                let field_clones = bis.iter().map(|bi| {
59                    let arg = &bi.expr;
60
61                    let clone = if let Some(clone_with) = bi.field.attrs.clone_with() {
62                        quote!(#clone_with(&#arg))
63                    } else {
64                        quote!(#arg.clone())
65                    };
66
67                    if let Some(ref name) = bi.field.ident {
68                        quote! {
69                            #name: #clone
70                        }
71                    } else {
72                        clone
73                    }
74                });
75
76                match style {
77                    ast::Style::Struct => {
78                        quote! {
79                            #arm_path {
80                                #(#field_clones),*
81                            }
82                        }
83                    }
84                    ast::Style::Tuple => {
85                        quote! {
86                            #arm_path (#(#field_clones),*)
87                        }
88                    }
89                    ast::Style::Unit => {
90                        quote! {
91                            #arm_path
92                        }
93                    }
94                }
95            },
96        );
97
98        let clone_from = if input.attrs.clone_from() {
99            Some(
100                matcher::Matcher::new(matcher::BindingStyle::RefMut, input.attrs.is_packed).build_arms(
101                    input,
102                    "__arg",
103                    |outer_arm_path, _, _, _, _, outer_bis| {
104                        let body = matcher::Matcher::new(matcher::BindingStyle::Ref, input.attrs.is_packed).build_arms(
105                            input,
106                            "__other",
107                            |inner_arm_path, _, _, _, _, inner_bis| {
108                                if outer_arm_path == inner_arm_path {
109                                    let field_clones = outer_bis.iter().zip(inner_bis).map(
110                                        |(outer_bi, inner_bi)| {
111                                            let outer = &outer_bi.expr;
112                                            let inner = &inner_bi.expr;
113
114                                            quote!(#outer.clone_from(&#inner);)
115                                        },
116                                    );
117
118                                    quote! {
119                                        #(#field_clones)*
120                                        return;
121                                    }
122                                } else {
123                                    quote!()
124                                }
125                            },
126                        );
127
128                        quote! {
129                            match *other {
130                                #body
131                            }
132                        }
133                    },
134                ),
135            )
136        } else {
137            None
138        };
139
140        let clone_from = clone_from.map(|body| {
141            // Enumerations are only cloned-from if both variants are the same.
142            // If they are different, fallback to normal cloning.
143            let fallback = if let ast::Body::Enum(_) = input.body {
144                Some(quote!(*self = other.clone();))
145            } else {
146                None
147            };
148
149            quote! {
150                #[allow(clippy::needless_return)]
151                fn clone_from(&mut self, other: &Self) {
152                    match *self {
153                        #body
154                    }
155
156                    #fallback
157                }
158            }
159        });
160
161        quote! {
162            #[allow(unused_qualifications)]
163            impl #impl_generics #clone_trait_path for #name #ty_generics #where_clause {
164                fn clone(&self) -> Self {
165                    match *self {
166                        #body
167                    }
168                }
169
170                #clone_from
171            }
172        }
173    }
174}
175
176fn needs_clone_bound(attrs: &attr::Field) -> bool {
177    attrs.clone_bound().is_none()
178}
179
180/// Return the path of the `Clone` trait, that is `::std::clone::Clone`.
181fn clone_trait_path() -> syn::Path {
182    if cfg!(feature = "use_core") {
183        parse_quote!(::core::clone::Clone)
184    } else {
185        parse_quote!(::std::clone::Clone)
186    }
187}
188
189/// Return the path of the `Copy` trait, that is `::std::marker::Copy`.
190fn copy_trait_path() -> syn::Path {
191    if cfg!(feature = "use_core") {
192        parse_quote!(::core::marker::Copy)
193    } else {
194        parse_quote!(::std::marker::Copy)
195    }
196}