1 | use proc_macro2; |
2 | |
3 | use ast; |
4 | use attr; |
5 | use matcher; |
6 | use syn; |
7 | use utils; |
8 | |
9 | /// Derive `Copy` for `input`. |
10 | pub 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 | ©_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`. |
30 | pub 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 | |
176 | fn 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`. |
181 | fn 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`. |
190 | fn 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 | |