]>
Commit | Line | Data |
---|---|---|
3b2f2976 XL |
1 | // Copyright 2017 Serde Developers |
2 | // | |
3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
4 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
5 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
6 | // option. This file may not be copied, modified, or distributed | |
7 | // except according to those terms. | |
8 | ||
9 | use std::collections::HashSet; | |
10 | ||
83c7162d XL |
11 | use syn; |
12 | use syn::punctuated::{Pair, Punctuated}; | |
13 | use syn::visit::{self, Visit}; | |
3b2f2976 | 14 | |
83c7162d | 15 | use internals::ast::{Container, Data}; |
3b2f2976 XL |
16 | use internals::attr; |
17 | ||
83c7162d | 18 | use proc_macro2::Span; |
3b2f2976 XL |
19 | |
20 | // Remove the default from every type parameter because in the generated impls | |
21 | // they look like associated types: "error: associated type bindings are not | |
22 | // allowed here". | |
23 | pub fn without_defaults(generics: &syn::Generics) -> syn::Generics { | |
24 | syn::Generics { | |
0531ce1d XL |
25 | params: generics |
26 | .params | |
3b2f2976 | 27 | .iter() |
0531ce1d | 28 | .map(|param| match *param { |
83c7162d XL |
29 | syn::GenericParam::Type(ref param) => syn::GenericParam::Type(syn::TypeParam { |
30 | eq_token: None, | |
31 | default: None, | |
32 | ..param.clone() | |
33 | }), | |
0531ce1d | 34 | _ => param.clone(), |
ff7c6d11 | 35 | }) |
3b2f2976 XL |
36 | .collect(), |
37 | ..generics.clone() | |
38 | } | |
39 | } | |
40 | ||
41 | pub fn with_where_predicates( | |
42 | generics: &syn::Generics, | |
43 | predicates: &[syn::WherePredicate], | |
44 | ) -> syn::Generics { | |
45 | let mut generics = generics.clone(); | |
83c7162d XL |
46 | generics |
47 | .make_where_clause() | |
3b2f2976 | 48 | .predicates |
0531ce1d | 49 | .extend(predicates.into_iter().cloned()); |
3b2f2976 XL |
50 | generics |
51 | } | |
52 | ||
83c7162d | 53 | pub fn with_where_predicates_from_fields( |
3b2f2976 XL |
54 | cont: &Container, |
55 | generics: &syn::Generics, | |
83c7162d XL |
56 | from_field: fn(&attr::Field) -> Option<&[syn::WherePredicate]>, |
57 | ) -> syn::Generics { | |
8faf50e0 XL |
58 | let predicates = cont |
59 | .data | |
3b2f2976 XL |
60 | .all_fields() |
61 | .flat_map(|field| from_field(&field.attrs)) | |
62 | .flat_map(|predicates| predicates.to_vec()); | |
63 | ||
64 | let mut generics = generics.clone(); | |
83c7162d | 65 | generics.make_where_clause().predicates.extend(predicates); |
3b2f2976 XL |
66 | generics |
67 | } | |
68 | ||
8faf50e0 XL |
69 | pub fn with_where_predicates_from_variants( |
70 | cont: &Container, | |
71 | generics: &syn::Generics, | |
72 | from_variant: fn(&attr::Variant) -> Option<&[syn::WherePredicate]>, | |
73 | ) -> syn::Generics { | |
74 | let variants = match cont.data { | |
75 | Data::Enum(ref variants) => variants, | |
76 | Data::Struct(_, _) => { | |
77 | return generics.clone(); | |
78 | } | |
79 | }; | |
80 | ||
81 | let predicates = variants | |
82 | .iter() | |
83 | .flat_map(|variant| from_variant(&variant.attrs)) | |
84 | .flat_map(|predicates| predicates.to_vec()); | |
85 | ||
86 | let mut generics = generics.clone(); | |
87 | generics.make_where_clause().predicates.extend(predicates); | |
88 | generics | |
89 | } | |
90 | ||
3b2f2976 XL |
91 | // Puts the given bound on any generic type parameters that are used in fields |
92 | // for which filter returns true. | |
93 | // | |
94 | // For example, the following struct needs the bound `A: Serialize, B: Serialize`. | |
95 | // | |
96 | // struct S<'b, A, B: 'b, C> { | |
97 | // a: A, | |
98 | // b: Option<&'b B> | |
99 | // #[serde(skip_serializing)] | |
100 | // c: C, | |
101 | // } | |
83c7162d | 102 | pub fn with_bound( |
3b2f2976 XL |
103 | cont: &Container, |
104 | generics: &syn::Generics, | |
83c7162d | 105 | filter: fn(&attr::Field, Option<&attr::Variant>) -> bool, |
3b2f2976 | 106 | bound: &syn::Path, |
83c7162d XL |
107 | ) -> syn::Generics { |
108 | struct FindTyParams<'ast> { | |
3b2f2976 XL |
109 | // Set of all generic type parameters on the current struct (A, B, C in |
110 | // the example). Initialized up front. | |
83c7162d XL |
111 | all_type_params: HashSet<syn::Ident>, |
112 | ||
3b2f2976 XL |
113 | // Set of generic type parameters used in fields for which filter |
114 | // returns true (A and B in the example). Filled in as the visitor sees | |
115 | // them. | |
83c7162d XL |
116 | relevant_type_params: HashSet<syn::Ident>, |
117 | ||
118 | // Fields whose type is an associated type of one of the generic type | |
119 | // parameters. | |
120 | associated_type_usage: Vec<&'ast syn::TypePath>, | |
3b2f2976 | 121 | } |
83c7162d XL |
122 | impl<'ast> Visit<'ast> for FindTyParams<'ast> { |
123 | fn visit_field(&mut self, field: &'ast syn::Field) { | |
124 | if let syn::Type::Path(ref ty) = field.ty { | |
125 | if let Some(Pair::Punctuated(ref t, _)) = ty.path.segments.first() { | |
126 | if self.all_type_params.contains(&t.ident) { | |
127 | self.associated_type_usage.push(ty); | |
128 | } | |
129 | } | |
130 | } | |
131 | self.visit_type(&field.ty); | |
132 | } | |
133 | ||
134 | fn visit_path(&mut self, path: &'ast syn::Path) { | |
3b2f2976 | 135 | if let Some(seg) = path.segments.last() { |
0531ce1d | 136 | if seg.into_value().ident == "PhantomData" { |
3b2f2976 XL |
137 | // Hardcoded exception, because PhantomData<T> implements |
138 | // Serialize and Deserialize whether or not T implements it. | |
139 | return; | |
140 | } | |
141 | } | |
0531ce1d | 142 | if path.leading_colon.is_none() && path.segments.len() == 1 { |
8faf50e0 XL |
143 | let id = &path.segments[0].ident; |
144 | if self.all_type_params.contains(id) { | |
145 | self.relevant_type_params.insert(id.clone()); | |
3b2f2976 XL |
146 | } |
147 | } | |
0531ce1d | 148 | visit::visit_path(self, path); |
3b2f2976 | 149 | } |
abe05a73 XL |
150 | |
151 | // Type parameter should not be considered used by a macro path. | |
152 | // | |
153 | // struct TypeMacro<T> { | |
154 | // mac: T!(), | |
155 | // marker: PhantomData<T>, | |
156 | // } | |
83c7162d | 157 | fn visit_macro(&mut self, _mac: &'ast syn::Macro) {} |
3b2f2976 XL |
158 | } |
159 | ||
8faf50e0 XL |
160 | let all_type_params = generics |
161 | .type_params() | |
162 | .map(|param| param.ident.clone()) | |
163 | .collect(); | |
3b2f2976 | 164 | |
3b2f2976 | 165 | let mut visitor = FindTyParams { |
83c7162d XL |
166 | all_type_params: all_type_params, |
167 | relevant_type_params: HashSet::new(), | |
168 | associated_type_usage: Vec::new(), | |
3b2f2976 | 169 | }; |
0531ce1d XL |
170 | match cont.data { |
171 | Data::Enum(ref variants) => for variant in variants.iter() { | |
ff7c6d11 XL |
172 | let relevant_fields = variant |
173 | .fields | |
174 | .iter() | |
175 | .filter(|field| filter(&field.attrs, Some(&variant.attrs))); | |
176 | for field in relevant_fields { | |
83c7162d | 177 | visitor.visit_field(field.original); |
ea8adc8c | 178 | } |
ff7c6d11 | 179 | }, |
0531ce1d | 180 | Data::Struct(_, ref fields) => { |
ea8adc8c | 181 | for field in fields.iter().filter(|field| filter(&field.attrs, None)) { |
83c7162d | 182 | visitor.visit_field(field.original); |
ea8adc8c XL |
183 | } |
184 | } | |
3b2f2976 XL |
185 | } |
186 | ||
83c7162d XL |
187 | let relevant_type_params = visitor.relevant_type_params; |
188 | let associated_type_usage = visitor.associated_type_usage; | |
3b2f2976 | 189 | let new_predicates = generics |
83c7162d | 190 | .type_params() |
8faf50e0 | 191 | .map(|param| param.ident.clone()) |
83c7162d XL |
192 | .filter(|id| relevant_type_params.contains(id)) |
193 | .map(|id| syn::TypePath { | |
194 | qself: None, | |
195 | path: id.into(), | |
0531ce1d | 196 | }) |
83c7162d XL |
197 | .chain(associated_type_usage.into_iter().cloned()) |
198 | .map(|bounded_ty| { | |
0531ce1d XL |
199 | syn::WherePredicate::Type(syn::PredicateType { |
200 | lifetimes: None, | |
ff7c6d11 | 201 | // the type parameter that is being bounded e.g. T |
83c7162d | 202 | bounded_ty: syn::Type::Path(bounded_ty), |
8faf50e0 | 203 | colon_token: <Token![:]>::default(), |
ff7c6d11 | 204 | // the bound e.g. Serialize |
83c7162d XL |
205 | bounds: vec![syn::TypeParamBound::Trait(syn::TraitBound { |
206 | paren_token: None, | |
207 | modifier: syn::TraitBoundModifier::None, | |
208 | lifetimes: None, | |
209 | path: bound.clone(), | |
210 | })].into_iter() | |
211 | .collect(), | |
ff7c6d11 XL |
212 | }) |
213 | }); | |
3b2f2976 XL |
214 | |
215 | let mut generics = generics.clone(); | |
83c7162d XL |
216 | generics |
217 | .make_where_clause() | |
0531ce1d XL |
218 | .predicates |
219 | .extend(new_predicates); | |
3b2f2976 XL |
220 | generics |
221 | } | |
222 | ||
223 | pub fn with_self_bound( | |
224 | cont: &Container, | |
225 | generics: &syn::Generics, | |
226 | bound: &syn::Path, | |
227 | ) -> syn::Generics { | |
228 | let mut generics = generics.clone(); | |
83c7162d XL |
229 | generics |
230 | .make_where_clause() | |
3b2f2976 | 231 | .predicates |
0531ce1d XL |
232 | .push(syn::WherePredicate::Type(syn::PredicateType { |
233 | lifetimes: None, | |
234 | // the type that is being bounded e.g. MyStruct<'a, T> | |
235 | bounded_ty: type_of_item(cont), | |
8faf50e0 | 236 | colon_token: <Token![:]>::default(), |
0531ce1d | 237 | // the bound e.g. Default |
83c7162d XL |
238 | bounds: vec![syn::TypeParamBound::Trait(syn::TraitBound { |
239 | paren_token: None, | |
240 | modifier: syn::TraitBoundModifier::None, | |
241 | lifetimes: None, | |
242 | path: bound.clone(), | |
243 | })].into_iter() | |
244 | .collect(), | |
0531ce1d | 245 | })); |
3b2f2976 XL |
246 | generics |
247 | } | |
248 | ||
249 | pub fn with_lifetime_bound(generics: &syn::Generics, lifetime: &str) -> syn::Generics { | |
83c7162d | 250 | let bound = syn::Lifetime::new(lifetime, Span::call_site()); |
0531ce1d XL |
251 | let def = syn::LifetimeDef { |
252 | attrs: Vec::new(), | |
8faf50e0 | 253 | lifetime: bound.clone(), |
0531ce1d XL |
254 | colon_token: None, |
255 | bounds: Punctuated::new(), | |
256 | }; | |
3b2f2976 | 257 | |
0531ce1d XL |
258 | let params = Some(syn::GenericParam::Lifetime(def)) |
259 | .into_iter() | |
83c7162d XL |
260 | .chain(generics.params.iter().cloned().map(|mut param| { |
261 | match param { | |
262 | syn::GenericParam::Lifetime(ref mut param) => { | |
8faf50e0 | 263 | param.bounds.push(bound.clone()); |
0531ce1d | 264 | } |
83c7162d | 265 | syn::GenericParam::Type(ref mut param) => { |
8faf50e0 XL |
266 | param |
267 | .bounds | |
268 | .push(syn::TypeParamBound::Lifetime(bound.clone())); | |
83c7162d XL |
269 | } |
270 | syn::GenericParam::Const(_) => {} | |
271 | } | |
272 | param | |
273 | })) | |
0531ce1d | 274 | .collect(); |
3b2f2976 | 275 | |
0531ce1d XL |
276 | syn::Generics { |
277 | params: params, | |
278 | ..generics.clone() | |
3b2f2976 | 279 | } |
3b2f2976 XL |
280 | } |
281 | ||
0531ce1d XL |
282 | fn type_of_item(cont: &Container) -> syn::Type { |
283 | syn::Type::Path(syn::TypePath { | |
284 | qself: None, | |
285 | path: syn::Path { | |
286 | leading_colon: None, | |
83c7162d | 287 | segments: vec![syn::PathSegment { |
8faf50e0 | 288 | ident: cont.ident.clone(), |
83c7162d XL |
289 | arguments: syn::PathArguments::AngleBracketed( |
290 | syn::AngleBracketedGenericArguments { | |
291 | colon2_token: None, | |
8faf50e0 XL |
292 | lt_token: <Token![<]>::default(), |
293 | args: cont | |
294 | .generics | |
83c7162d XL |
295 | .params |
296 | .iter() | |
297 | .map(|param| match *param { | |
298 | syn::GenericParam::Type(ref param) => { | |
299 | syn::GenericArgument::Type(syn::Type::Path(syn::TypePath { | |
300 | qself: None, | |
8faf50e0 | 301 | path: param.ident.clone().into(), |
83c7162d XL |
302 | })) |
303 | } | |
304 | syn::GenericParam::Lifetime(ref param) => { | |
8faf50e0 | 305 | syn::GenericArgument::Lifetime(param.lifetime.clone()) |
83c7162d XL |
306 | } |
307 | syn::GenericParam::Const(_) => { | |
308 | panic!("Serde does not support const generics yet"); | |
309 | } | |
310 | }) | |
311 | .collect(), | |
8faf50e0 | 312 | gt_token: <Token![>]>::default(), |
83c7162d XL |
313 | }, |
314 | ), | |
315 | }].into_iter() | |
316 | .collect(), | |
3b2f2976 | 317 | }, |
0531ce1d | 318 | }) |
3b2f2976 | 319 | } |