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: &Ident = &input.ident;
12
13 let copy_trait_path: Path = copy_trait_path();
14 let generics: Generics = utils::build_impl_generics(
15 item:input,
16 &copy_trait_path,
17 |attrs| attrs.copy_bound().is_none(),
18 |field| field.copy_bound(),
19 |input: &Input| input.copy_bound(),
20 );
21 let (impl_generics: ImplGenerics<'_>, ty_generics: TypeGenerics<'_>, where_clause: Option<&WhereClause>) = 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}
197