From c41377557464766f4510752ee7f64a95ae84169e Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 9 Apr 2018 23:22:18 -0400 Subject: [PATCH 1/6] Add partially-working where bounds for associated types --- serde_derive/src/bound.rs | 17 ++++++++++++++--- serde_derive/src/de.rs | 7 ++++--- serde_derive/src/ser.rs | 5 +++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/serde_derive/src/bound.rs b/serde_derive/src/bound.rs index 4e7c7d71b..74d628c5a 100644 --- a/serde_derive/src/bound.rs +++ b/serde_derive/src/bound.rs @@ -50,18 +50,29 @@ pub fn with_where_predicates( generics } -pub fn with_where_predicates_from_fields( +pub fn with_where_predicates_from_fields( cont: &Container, generics: &syn::Generics, + trait_bound: &syn::Path, from_field: F, + where_ty: W, ) -> syn::Generics where F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, + W: for<'a> Fn(&attr::Field) -> Option<&syn::ExprPath>, { let predicates = cont.data .all_fields() - .flat_map(|field| from_field(&field.attrs)) - .flat_map(|predicates| predicates.to_vec()); + .flat_map(|field| { + let field_bound: Option = match where_ty(&field.attrs) { + Some(_) => None, + None => { + let field_ty = field.ty; + Some(parse_quote!(#field_ty: #trait_bound)) + } + }; + field_bound.into_iter().chain(from_field(&field.attrs).into_iter().flat_map(|predicates| predicates.to_vec())) + }); let mut generics = generics.clone(); generics.make_where_clause() diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 941c624a1..975f95c7c 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -124,7 +124,9 @@ impl Parameters { fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generics { let generics = bound::without_defaults(cont.generics); - let generics = bound::with_where_predicates_from_fields(cont, &generics, attr::Field::de_bound); + let delife = borrowed.de_lifetime(); + let de_bound = parse_quote!(_serde::Deserialize<#delife>); + let generics = bound::with_where_predicates_from_fields(cont, &generics, &de_bound, attr::Field::de_bound, attr::Field::deserialize_with); match cont.attrs.de_bound() { Some(predicates) => bound::with_where_predicates(&generics, predicates), @@ -136,12 +138,11 @@ fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generi attr::Default::None | attr::Default::Path(_) => generics, }; - let delife = borrowed.de_lifetime(); let generics = bound::with_bound( cont, &generics, needs_deserialize_bound, - &parse_quote!(_serde::Deserialize<#delife>), + &de_bound, ); bound::with_bound( diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 8bcacd6a4..07b88d818 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -130,8 +130,9 @@ impl Parameters { fn build_generics(cont: &Container) -> syn::Generics { let generics = bound::without_defaults(cont.generics); + let trait_bound = parse_quote!(_serde::Serialize); let generics = - bound::with_where_predicates_from_fields(cont, &generics, attr::Field::ser_bound); + bound::with_where_predicates_from_fields(cont, &generics, &trait_bound, attr::Field::ser_bound, attr::Field::serialize_with); match cont.attrs.ser_bound() { Some(predicates) => bound::with_where_predicates(&generics, predicates), @@ -139,7 +140,7 @@ fn build_generics(cont: &Container) -> syn::Generics { cont, &generics, needs_serialize_bound, - &parse_quote!(_serde::Serialize), + &trait_bound ), } } From fd143327299eee966149edf1a05b01aa852fe7be Mon Sep 17 00:00:00 2001 From: Osspial Date: Mon, 9 Apr 2018 23:43:49 -0400 Subject: [PATCH 2/6] Ignore skipped fields --- serde_derive/src/bound.rs | 15 ++++++++------- serde_derive/src/de.rs | 8 +++++++- serde_derive/src/ser.rs | 9 +++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/serde_derive/src/bound.rs b/serde_derive/src/bound.rs index 74d628c5a..feafd1c33 100644 --- a/serde_derive/src/bound.rs +++ b/serde_derive/src/bound.rs @@ -55,21 +55,22 @@ pub fn with_where_predicates_from_fields( generics: &syn::Generics, trait_bound: &syn::Path, from_field: F, - where_ty: W, + gen_bound_where: W, ) -> syn::Generics where F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, - W: for<'a> Fn(&attr::Field) -> Option<&syn::ExprPath>, + W: for<'a> Fn(&attr::Field) -> bool, { let predicates = cont.data .all_fields() .flat_map(|field| { - let field_bound: Option = match where_ty(&field.attrs) { - Some(_) => None, - None => { + let field_bound: Option = match gen_bound_where(&field.attrs) { + true => { let field_ty = field.ty; - Some(parse_quote!(#field_ty: #trait_bound)) - } + let predicate: syn::WherePredicate = parse_quote!(#field_ty: #trait_bound); + Some(predicate) + }, + false => None }; field_bound.into_iter().chain(from_field(&field.attrs).into_iter().flat_map(|predicates| predicates.to_vec())) }); diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 975f95c7c..f929397cd 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -126,7 +126,13 @@ fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generi let delife = borrowed.de_lifetime(); let de_bound = parse_quote!(_serde::Deserialize<#delife>); - let generics = bound::with_where_predicates_from_fields(cont, &generics, &de_bound, attr::Field::de_bound, attr::Field::deserialize_with); + let generics = bound::with_where_predicates_from_fields( + cont, + &generics, + &de_bound, + attr::Field::de_bound, + |field| field.deserialize_with().is_none() && !field.skip_deserializing() + ); match cont.attrs.de_bound() { Some(predicates) => bound::with_where_predicates(&generics, predicates), diff --git a/serde_derive/src/ser.rs b/serde_derive/src/ser.rs index 07b88d818..7e37ca6b4 100644 --- a/serde_derive/src/ser.rs +++ b/serde_derive/src/ser.rs @@ -131,8 +131,13 @@ fn build_generics(cont: &Container) -> syn::Generics { let generics = bound::without_defaults(cont.generics); let trait_bound = parse_quote!(_serde::Serialize); - let generics = - bound::with_where_predicates_from_fields(cont, &generics, &trait_bound, attr::Field::ser_bound, attr::Field::serialize_with); + let generics = bound::with_where_predicates_from_fields( + cont, + &generics, + &trait_bound, + attr::Field::ser_bound, + |field| field.serialize_with().is_none() && !field.skip_serializing() + ); match cont.attrs.ser_bound() { Some(predicates) => bound::with_where_predicates(&generics, predicates), From 2e824e9abacefd8b31235f94b80f1b2fbc491000 Mon Sep 17 00:00:00 2001 From: Osspial Date: Tue, 10 Apr 2018 10:53:37 -0400 Subject: [PATCH 3/6] Limit type bounds to associated types --- serde_derive/src/bound.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/serde_derive/src/bound.rs b/serde_derive/src/bound.rs index feafd1c33..6cfee5f2b 100644 --- a/serde_derive/src/bound.rs +++ b/serde_derive/src/bound.rs @@ -64,13 +64,18 @@ where let predicates = cont.data .all_fields() .flat_map(|field| { - let field_bound: Option = match gen_bound_where(&field.attrs) { - true => { - let field_ty = field.ty; - let predicate: syn::WherePredicate = parse_quote!(#field_ty: #trait_bound); - Some(predicate) + let field_ty = field.ty; + let field_bound: Option = match (field_ty, gen_bound_where(&field.attrs)) { + (&syn::Type::Path(ref ty_path), true) => match (ty_path.path.segments.first(), generics.params.first()) { + (Some(syn::punctuated::Pair::Punctuated(ref t, _)), Some(syn::punctuated::Pair::End(&syn::GenericParam::Type(ref generic_ty)))) + if generic_ty.ident == t.ident => + { + let predicate: syn::WherePredicate = parse_quote!(#field_ty: #trait_bound); + Some(predicate) + }, + _ => None }, - false => None + (_, _) => None }; field_bound.into_iter().chain(from_field(&field.attrs).into_iter().flat_map(|predicates| predicates.to_vec())) }); From def8d6e8afc2dcbbd96d3db486b286c295ef46df Mon Sep 17 00:00:00 2001 From: Osspial Date: Tue, 10 Apr 2018 11:16:23 -0400 Subject: [PATCH 4/6] Add associated type test --- test_suite/tests/test_gen.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test_suite/tests/test_gen.rs b/test_suite/tests/test_gen.rs index 368403e77..a524c7720 100644 --- a/test_suite/tests/test_gen.rs +++ b/test_suite/tests/test_gen.rs @@ -539,6 +539,22 @@ fn test_gen() { array: [u8; 256], } assert_ser::(); + + trait AssocSerde { + type Assoc; + } + + struct NoSerdeImpl; + impl AssocSerde for NoSerdeImpl { + type Assoc = u32; + } + + #[derive(Serialize, Deserialize)] + struct AssocDerive { + assoc: T::Assoc + } + + assert::>(); } ////////////////////////////////////////////////////////////////////////// From 4415d10c61af779ec65a05dd34834e5ff8b5990c Mon Sep 17 00:00:00 2001 From: Osspial Date: Thu, 12 Apr 2018 16:44:32 -0400 Subject: [PATCH 5/6] Fix associated types only working for first generic parameter Also, removes extraneous `where for` --- serde_derive/src/bound.rs | 27 +++++++++++++++------------ test_suite/tests/test_gen.rs | 8 ++++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/serde_derive/src/bound.rs b/serde_derive/src/bound.rs index 6cfee5f2b..28772cbe1 100644 --- a/serde_derive/src/bound.rs +++ b/serde_derive/src/bound.rs @@ -59,24 +59,27 @@ pub fn with_where_predicates_from_fields( ) -> syn::Generics where F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, - W: for<'a> Fn(&attr::Field) -> bool, + W: Fn(&attr::Field) -> bool, { let predicates = cont.data .all_fields() .flat_map(|field| { let field_ty = field.ty; - let field_bound: Option = match (field_ty, gen_bound_where(&field.attrs)) { - (&syn::Type::Path(ref ty_path), true) => match (ty_path.path.segments.first(), generics.params.first()) { - (Some(syn::punctuated::Pair::Punctuated(ref t, _)), Some(syn::punctuated::Pair::End(&syn::GenericParam::Type(ref generic_ty)))) - if generic_ty.ident == t.ident => - { - let predicate: syn::WherePredicate = parse_quote!(#field_ty: #trait_bound); - Some(predicate) - }, - _ => None - }, - (_, _) => None + let matching_generic = |t: &syn::PathSegment, g| match g { + &syn::GenericParam::Type(ref generic_ty) + if generic_ty.ident == t.ident => true, + _ => false }; + + let mut field_bound: Option = None; + if let &syn::Type::Path(ref ty_path) = field_ty { + field_bound = match (gen_bound_where(&field.attrs), ty_path.path.segments.first()) { + (true, Some(syn::punctuated::Pair::Punctuated(ref t, _))) + if generics.params.iter().fold(false, |b, g| b || matching_generic(t, g)) + => Some(parse_quote!(#field_ty: #trait_bound)), + (_, _) => None + }; + } field_bound.into_iter().chain(from_field(&field.attrs).into_iter().flat_map(|predicates| predicates.to_vec())) }); diff --git a/test_suite/tests/test_gen.rs b/test_suite/tests/test_gen.rs index a524c7720..aa5e0c4c8 100644 --- a/test_suite/tests/test_gen.rs +++ b/test_suite/tests/test_gen.rs @@ -555,6 +555,14 @@ fn test_gen() { } assert::>(); + + #[derive(Serialize, Deserialize)] + struct AssocDeriveMulti { + s: S, + assoc: T::Assoc, + } + + assert::>(); } ////////////////////////////////////////////////////////////////////////// From 629bf7b3541ee78f7c275ed68cdb34f49c60f783 Mon Sep 17 00:00:00 2001 From: Osspial Date: Thu, 12 Apr 2018 18:19:18 -0400 Subject: [PATCH 6/6] Fix clippy warnings --- serde_derive/src/bound.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/serde_derive/src/bound.rs b/serde_derive/src/bound.rs index 28772cbe1..1506a9ba2 100644 --- a/serde_derive/src/bound.rs +++ b/serde_derive/src/bound.rs @@ -65,18 +65,19 @@ where .all_fields() .flat_map(|field| { let field_ty = field.ty; - let matching_generic = |t: &syn::PathSegment, g| match g { - &syn::GenericParam::Type(ref generic_ty) + let matching_generic = |t: &syn::PathSegment, g: &syn::GenericParam| match *g { + syn::GenericParam::Type(ref generic_ty) if generic_ty.ident == t.ident => true, _ => false }; let mut field_bound: Option = None; - if let &syn::Type::Path(ref ty_path) = field_ty { + if let syn::Type::Path(ref ty_path) = *field_ty { field_bound = match (gen_bound_where(&field.attrs), ty_path.path.segments.first()) { - (true, Some(syn::punctuated::Pair::Punctuated(ref t, _))) - if generics.params.iter().fold(false, |b, g| b || matching_generic(t, g)) - => Some(parse_quote!(#field_ty: #trait_bound)), + (true, Some(syn::punctuated::Pair::Punctuated(ref t, _))) => + if generics.params.iter().any(|g| matching_generic(t, g)) { + Some(parse_quote!(#field_ty: #trait_bound)) + } else {None}, (_, _) => None }; }