1
+ use std:: collections:: HashSet ;
2
+
1
3
use aster;
2
4
3
5
use syntax:: ast:: {
@@ -10,6 +12,7 @@ use syntax::codemap::Span;
10
12
use syntax:: ext:: base:: { Annotatable , ExtCtxt } ;
11
13
use syntax:: ext:: build:: AstBuilder ;
12
14
use syntax:: ptr:: P ;
15
+ use syntax:: visit;
13
16
14
17
use attr;
15
18
use error:: Error ;
@@ -85,8 +88,8 @@ fn serialize_item(
85
88
) . unwrap ( ) )
86
89
}
87
90
88
- // All the generics in the input, plus a bound `T: Serialize` for each field
89
- // type that will be serialized by us.
91
+ // All the generics in the input, plus a bound `T: Serialize` for each generic
92
+ // field type that will be serialized by us.
90
93
fn build_impl_generics (
91
94
cx : & ExtCtxt ,
92
95
builder : & aster:: AstBuilder ,
@@ -104,6 +107,8 @@ fn build_impl_generics(
104
107
. flat_map ( |variant_data| all_struct_fields ( variant_data) )
105
108
. filter ( |field| serialized_by_us ( field) )
106
109
. map ( |field| & field. node . ty )
110
+ // TODO this filter can be removed later, see comment on function
111
+ . filter ( |ty| contains_generic ( ty, generics) )
107
112
. map ( |ty| strip_reference ( ty) )
108
113
. map ( |ty| builder. where_predicate ( )
109
114
// the type that is being bounded i.e. T
@@ -144,8 +149,8 @@ fn all_struct_fields(variant_data: &ast::VariantData) -> &[ast::StructField] {
144
149
}
145
150
146
151
// Fields with a `skip_serializing` or `serialize_with` attribute are not
147
- // serialized by us. All other fields will receive a `T: Serialize` bound where
148
- // T is the type of the field.
152
+ // serialized by us. All other fields may need a `T: Serialize` bound where T is
153
+ // the type of the field.
149
154
fn serialized_by_us ( field : & ast:: StructField ) -> bool {
150
155
for meta_items in field. node . attrs . iter ( ) . filter_map ( attr:: get_serde_meta_items) {
151
156
for meta_item in meta_items {
@@ -163,6 +168,44 @@ fn serialized_by_us(field: &ast::StructField) -> bool {
163
168
true
164
169
}
165
170
171
+ // Rust <1.7 enforces that `where` clauses involve generic type parameters. The
172
+ // corresponding compiler error is E0193. It is no longer enforced in Rust >=1.7
173
+ // so this filtering can be removed in the future when we stop supporting <1.7.
174
+ //
175
+ // E0193 means we must not generate a `where` clause like `i32: Serialize`
176
+ // because even though i32 implements Serialize, i32 is not a generic type
177
+ // parameter. Clauses like `T: Serialize` and `Option<T>: Serialize` are okay.
178
+ // This function decides whether a given type references any of the generic type
179
+ // parameters in the input `Generics`.
180
+ fn contains_generic ( ty : & ast:: Ty , generics : & ast:: Generics ) -> bool {
181
+ struct FindGeneric < ' a > {
182
+ generic_names : & ' a HashSet < ast:: Name > ,
183
+ found_generic : bool ,
184
+ }
185
+ impl < ' a , ' v > visit:: Visitor < ' v > for FindGeneric < ' a > {
186
+ fn visit_path ( & mut self , path : & ' v ast:: Path , _id : ast:: NodeId ) {
187
+ if !path. global
188
+ && path. segments . len ( ) == 1
189
+ && self . generic_names . contains ( & path. segments [ 0 ] . identifier . name ) {
190
+ self . found_generic = true ;
191
+ } else {
192
+ visit:: walk_path ( self , path) ;
193
+ }
194
+ }
195
+ }
196
+
197
+ let generic_names: HashSet < _ > = generics. ty_params . iter ( )
198
+ . map ( |ty_param| ty_param. ident . name )
199
+ . collect ( ) ;
200
+
201
+ let mut visitor = FindGeneric {
202
+ generic_names : & generic_names,
203
+ found_generic : false ,
204
+ } ;
205
+ visit:: walk_ty ( & mut visitor, ty) ;
206
+ visitor. found_generic
207
+ }
208
+
166
209
// This is required to handle types that use both a reference and a value of
167
210
// the same type, as in:
168
211
//
0 commit comments