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