From ab5e8780abe213a4d9fd4915aa87d7662046b647 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Mon, 13 Nov 2017 13:37:36 -0500 Subject: [PATCH 01/17] Add deserialize_from to Deserialize --- serde/src/de/mod.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index 848183c7..aa1cc3af 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -504,6 +504,35 @@ pub trait Deserialize<'de>: Sized { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>; + + /// Deserializes a value into `self` from the given Deserializer. + /// + /// The purpose of this method is to allow the deserializer to reuse + /// resources and avoid copies. As such, if this method returns an error, + /// `self` will be in an indeterminate state where some parts of the struct + /// have been overwritten. Although whatever state that is will be + /// memory-safe. + /// + /// This is generally useful when repeateadly deserializing values that + /// are processed one at a time, where the value of `self` doesn't matter + /// when the next deserialization occurs. + /// + /// If you manually implement this, your recursive deserializations should + /// use `deserialize_from`. + /// + /// TODO: example + /// + /// ``` + /// // Something with a loop that returns on error. + /// + /// ``` + fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error> + where D: Deserializer<'de> + { + // Default implementation just delegates to `deserialize` impl. + *self = Deserialize::deserialize(deserializer)?; + Ok(()) + } } /// A data structure that can be deserialized without borrowing any data from From bc221abb04ed942afed87a41147802f1c7d3955a Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Mon, 13 Nov 2017 13:38:27 -0500 Subject: [PATCH 02/17] Augment builtin std/core Deserialize impls to implement deserialize_from --- serde/src/de/impls.rs | 338 +++++++++++++++++++++++++++++++++------- serde/src/private/de.rs | 19 ++- 2 files changed, 304 insertions(+), 53 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index ea48d34e..c7ea2ede 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -15,6 +15,7 @@ use de::{Deserialize, Deserializer, EnumAccess, Error, SeqAccess, Unexpected, Va use de::MapAccess; use de::from_primitive::FromPrimitive; +use private::de::DeserializeFromSeed; #[cfg(any(feature = "std", feature = "alloc"))] use private::de::size_hint; @@ -51,6 +52,7 @@ impl<'de> Deserialize<'de> for () { struct BoolVisitor; + impl<'de> Visitor<'de> for BoolVisitor { type Value = bool; @@ -210,6 +212,8 @@ impl<'de> Deserialize<'de> for char { #[cfg(any(feature = "std", feature = "alloc"))] struct StringVisitor; +#[cfg(any(feature = "std", feature = "alloc"))] +struct StringFromVisitor<'a>(&'a mut String); #[cfg(any(feature = "std", feature = "alloc"))] impl<'de> Visitor<'de> for StringVisitor { @@ -254,6 +258,59 @@ impl<'de> Visitor<'de> for StringVisitor { } } +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a, 'de> Visitor<'de> for StringFromVisitor<'a> { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string") + } + + fn visit_str(self, v: &str) -> Result<(), E> + where + E: Error, + { + self.0.clear(); + self.0.push_str(v); + Ok(()) + } + + fn visit_string(self, v: String) -> Result<(), E> + where + E: Error, + { + *self.0 = v; + Ok(()) + } + + fn visit_bytes(self, v: &[u8]) -> Result<(), E> + where + E: Error, + { + match str::from_utf8(v) { + Ok(s) => { + self.0.clear(); + self.0.push_str(s); + Ok(()) + } + Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)), + } + } + + fn visit_byte_buf(self, v: Vec) -> Result<(), E> + where + E: Error, + { + match String::from_utf8(v) { + Ok(s) => { + *self.0 = s; + Ok(()) + } + Err(e) => Err(Error::invalid_value(Unexpected::Bytes(&e.into_bytes()), &self),), + } + } +} + #[cfg(any(feature = "std", feature = "alloc"))] impl<'de> Deserialize<'de> for String { fn deserialize(deserializer: D) -> Result @@ -262,6 +319,13 @@ impl<'de> Deserialize<'de> for String { { deserializer.deserialize_string(StringVisitor) } + + fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_string(StringFromVisitor(self)) + } } //////////////////////////////////////////////////////////////////////////////// @@ -421,6 +485,7 @@ forwarded_impl!((), Box, CString::into_boxed_c_str); struct OptionVisitor { marker: PhantomData, } +struct OptionFromVisitor<'a, T: 'a>(&'a mut Option); impl<'de, T> Visitor<'de> for OptionVisitor where @@ -457,6 +522,49 @@ where } } +impl<'a, 'de, T> Visitor<'de> for OptionFromVisitor<'a, T> +where + T: Deserialize<'de>, +{ + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("option") + } + + #[inline] + fn visit_unit(self) -> Result<(), E> + where + E: Error, + { + *self.0 = None; + Ok(()) + } + + #[inline] + fn visit_none(self) -> Result<(), E> + where + E: Error, + { + *self.0 = None; + Ok(()) + } + + #[inline] + fn visit_some(self, deserializer: D) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + // The some enum's repr is opaque, so we can't play cute tricks with + // its tag to build this in place unconditionally. + // + // FIXME: investigate whether branching on the old value being Some to + // deserialize_from the value is profitable (probably data-dependent?) + *self.0 = try!(T::deserialize(deserializer).map(Some)); + Ok(()) + } +} + impl<'de, T> Deserialize<'de> for Option where T: Deserialize<'de>, @@ -467,6 +575,13 @@ where { deserializer.deserialize_option(OptionVisitor { marker: PhantomData }) } + + fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_option(OptionFromVisitor(self)) + } } //////////////////////////////////////////////////////////////////////////////// @@ -509,7 +624,9 @@ macro_rules! seq_impl { $ty:ident < T $(: $tbound1:ident $(+ $tbound2:ident)*)* $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)*)* >, $access:ident, $ctor:expr, + $clear:expr, $with_capacity:expr, + $reserve:expr, $insert:expr ) => { impl<'de, T $(, $typaram)*> Deserialize<'de> for $ty @@ -554,16 +671,59 @@ macro_rules! seq_impl { let visitor = SeqVisitor { marker: PhantomData }; deserializer.deserialize_seq(visitor) } + + fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + struct SeqVisitor<'a, T: 'a $(, $typaram: 'a)*>(&'a mut $ty); + + impl<'a, 'de, T $(, $typaram)*> Visitor<'de> for SeqVisitor<'a, T $(, $typaram)*> + where + T: Deserialize<'de> $(+ $tbound1 $(+ $tbound2)*)*, + $($typaram: $bound1 $(+ $bound2)*,)* + { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a sequence") + } + + #[inline] + fn visit_seq(mut self, mut $access: A) -> Result<(), A::Error> + where + A: SeqAccess<'de>, + { + $clear(&mut self.0); + $reserve(&mut self.0, size_hint::cautious($access.size_hint())); + + // FIXME: try to overwrite old values here? (Vec, VecDeque, LinkedList) + while let Some(value) = try!($access.next_element()) { + $insert(&mut self.0, value); + } + + Ok(()) + } + } + + deserializer.deserialize_seq(SeqVisitor(self)) + } } } } +// Dummy impl of reserve +#[cfg(any(feature = "std", feature = "alloc"))] +fn nop_reserve(_x: T, _y: U) { } + #[cfg(any(feature = "std", feature = "alloc"))] seq_impl!( BinaryHeap, seq, BinaryHeap::new(), + BinaryHeap::clear, BinaryHeap::with_capacity(size_hint::cautious(seq.size_hint())), + BinaryHeap::reserve, BinaryHeap::push); #[cfg(any(feature = "std", feature = "alloc"))] @@ -571,7 +731,9 @@ seq_impl!( BTreeSet, seq, BTreeSet::new(), + BTreeSet::clear, BTreeSet::new(), + nop_reserve, BTreeSet::insert); #[cfg(any(feature = "std", feature = "alloc"))] @@ -579,7 +741,9 @@ seq_impl!( LinkedList, seq, LinkedList::new(), + LinkedList::clear, LinkedList::new(), + nop_reserve, LinkedList::push_back); #[cfg(feature = "std")] @@ -587,7 +751,9 @@ seq_impl!( HashSet, seq, HashSet::with_hasher(S::default()), + HashSet::clear, HashSet::with_capacity_and_hasher(size_hint::cautious(seq.size_hint()), S::default()), + HashSet::reserve, HashSet::insert); #[cfg(any(feature = "std", feature = "alloc"))] @@ -595,7 +761,9 @@ seq_impl!( Vec, seq, Vec::new(), + Vec::clear, Vec::with_capacity(size_hint::cautious(seq.size_hint())), + Vec::reserve, Vec::push); #[cfg(any(feature = "std", feature = "alloc"))] @@ -603,7 +771,9 @@ seq_impl!( VecDeque, seq, VecDeque::new(), + VecDeque::clear, VecDeque::with_capacity(size_hint::cautious(seq.size_hint())), + VecDeque::reserve, VecDeque::push_back); //////////////////////////////////////////////////////////////////////////////// @@ -611,6 +781,7 @@ seq_impl!( struct ArrayVisitor { marker: PhantomData, } +struct ArrayFromVisitor<'a, A: 'a>(&'a mut A); impl ArrayVisitor { fn new() -> Self { @@ -673,6 +844,35 @@ macro_rules! array_impls { } } + impl<'a, 'de, T> Visitor<'de> for ArrayFromVisitor<'a, [T; $len]> + where + T: Deserialize<'de>, + { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("an array of length ", $len)) + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result<(), A::Error> + where + A: SeqAccess<'de>, + { + let mut fail_idx = None; + for (idx, dest) in self.0[..].iter_mut().enumerate() { + if try!(seq.next_element_seed(DeserializeFromSeed(dest))).is_none() { + fail_idx = Some(idx); + break; + } + } + if let Some(idx) = fail_idx { + return Err(Error::invalid_length(idx, &self)); + } + Ok(()) + } + } + impl<'de, T> Deserialize<'de> for [T; $len] where T: Deserialize<'de>, @@ -683,6 +883,13 @@ macro_rules! array_impls { { deserializer.deserialize_tuple($len, ArrayVisitor::<[T; $len]>::new()) } + + fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_tuple($len, ArrayFromVisitor(self)) + } } )+ } @@ -726,49 +933,76 @@ array_impls! { //////////////////////////////////////////////////////////////////////////////// macro_rules! tuple_impls { - ($($len:tt $visitor:ident => ($($n:tt $name:ident)+))+) => { + ($($len:tt => ($($n:tt $name:ident)+))+) => { $( - struct $visitor<$($name,)+> { - marker: PhantomData<($($name,)+)>, - } - - impl<$($name,)+> $visitor<$($name,)+> { - fn new() -> Self { - $visitor { marker: PhantomData } - } - } - - impl<'de, $($name: Deserialize<'de>),+> Visitor<'de> for $visitor<$($name,)+> { - type Value = ($($name,)+); - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str(concat!("a tuple of size ", $len)) - } - - #[inline] - #[allow(non_snake_case)] - fn visit_seq(self, mut seq: A) -> Result<($($name,)+), A::Error> - where - A: SeqAccess<'de>, - { - $( - let $name = match try!(seq.next_element()) { - Some(value) => value, - None => return Err(Error::invalid_length($n, &self)), - }; - )+ - - Ok(($($name,)+)) - } - } - impl<'de, $($name: Deserialize<'de>),+> Deserialize<'de> for ($($name,)+) { #[inline] fn deserialize(deserializer: D) -> Result<($($name,)+), D::Error> where D: Deserializer<'de>, { - deserializer.deserialize_tuple($len, $visitor::new()) + struct TupleVisitor<$($name,)+> { + marker: PhantomData<($($name,)+)>, + } + + impl<'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleVisitor<$($name,)+> { + type Value = ($($name,)+); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("a tuple of size ", $len)) + } + + #[inline] + #[allow(non_snake_case)] + fn visit_seq(self, mut seq: A) -> Result<($($name,)+), A::Error> + where + A: SeqAccess<'de>, + { + $( + let $name = match try!(seq.next_element()) { + Some(value) => value, + None => return Err(Error::invalid_length($n, &self)), + }; + )+ + + Ok(($($name,)+)) + } + } + + deserializer.deserialize_tuple($len, TupleVisitor { marker: PhantomData }) + } + + #[inline] + fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + struct TupleVisitor<'a, $($name: 'a,)+>(&'a mut ($($name,)+)); + + impl<'a, 'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleVisitor<'a, $($name,)+> { + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(concat!("a tuple of size ", $len)) + } + + #[inline] + #[allow(non_snake_case)] + fn visit_seq(self, mut seq: A) -> Result<(), A::Error> + where + A: SeqAccess<'de>, + { + $( + if try!(seq.next_element_seed(DeserializeFromSeed(&mut (self.0).$n))).is_none() { + return Err(Error::invalid_length($n, &self)); + } + )+ + + Ok(()) + } + } + + deserializer.deserialize_tuple($len, TupleVisitor(self)) } } )+ @@ -776,22 +1010,22 @@ macro_rules! tuple_impls { } tuple_impls! { - 1 TupleVisitor1 => (0 T0) - 2 TupleVisitor2 => (0 T0 1 T1) - 3 TupleVisitor3 => (0 T0 1 T1 2 T2) - 4 TupleVisitor4 => (0 T0 1 T1 2 T2 3 T3) - 5 TupleVisitor5 => (0 T0 1 T1 2 T2 3 T3 4 T4) - 6 TupleVisitor6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5) - 7 TupleVisitor7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6) - 8 TupleVisitor8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7) - 9 TupleVisitor9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8) - 10 TupleVisitor10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9) - 11 TupleVisitor11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10) - 12 TupleVisitor12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11) - 13 TupleVisitor13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12) - 14 TupleVisitor14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13) - 15 TupleVisitor15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14) - 16 TupleVisitor16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15) + 1 => (0 T0) + 2 => (0 T0 1 T1) + 3 => (0 T0 1 T1 2 T2) + 4 => (0 T0 1 T1 2 T2 3 T3) + 5 => (0 T0 1 T1 2 T2 3 T3 4 T4) + 6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5) + 7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6) + 8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7) + 9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8) + 10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9) + 11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10) + 12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11) + 13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12) + 14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13) + 15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14) + 16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15) } //////////////////////////////////////////////////////////////////////////////// diff --git a/serde/src/private/de.rs b/serde/src/private/de.rs index 7c98d9f9..a3134e69 100644 --- a/serde/src/private/de.rs +++ b/serde/src/private/de.rs @@ -8,7 +8,7 @@ use lib::*; -use de::{Deserialize, Deserializer, IntoDeserializer, Error, Visitor}; +use de::{Deserialize, Deserializer, DeserializeSeed, IntoDeserializer, Error, Visitor}; #[cfg(any(feature = "std", feature = "alloc"))] use de::Unexpected; @@ -2009,3 +2009,20 @@ where map struct enum identifier ignored_any } } + +/// A DeserializeSeed helper for implementing deserialize_from Visitors. +/// +/// Wraps a mutable reference and calls deserialize_from on it. +pub struct DeserializeFromSeed<'a, T: 'a>(pub &'a mut T); + +impl<'a, 'de, T> DeserializeSeed<'de> for DeserializeFromSeed<'a, T> + where T: Deserialize<'de>, +{ + type Value = (); + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + self.0.deserialize_from(deserializer) + } +} From e354dd0c7f59e05bc829a4c1e7fd26e657e9354e Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Mon, 13 Nov 2017 15:35:14 -0500 Subject: [PATCH 03/17] Derive deserialize_from for tuples and structs This adds a new "deserialize_from" feature (default off) that opts into deriving deserialize_from with #[derive(Deserialize)]. --- serde_derive/Cargo.toml | 4 + serde_derive/src/de.rs | 616 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 617 insertions(+), 3 deletions(-) diff --git a/serde_derive/Cargo.toml b/serde_derive/Cargo.toml index 62b6e4cb..1b868d8d 100644 --- a/serde_derive/Cargo.toml +++ b/serde_derive/Cargo.toml @@ -14,6 +14,10 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE- [badges] travis-ci = { repository = "serde-rs/serde" } +[features] +default = [] +deserialize_from = [] + [lib] name = "serde_derive" proc-macro = true diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index bfa18b00..04b0929a 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -25,7 +25,7 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result Result(__deserializer: __D) -> _serde::export::Result<#remote #ty_generics, __D::Error> where __D: _serde::Deserializer<#delife> { - #body + #main_body } } } } else { + let from_body = deserialize_from_body(&cont, ¶ms); + let from_impl = from_body.map(|from_body| { + let from_body = Stmts(from_body); + + quote! { + fn deserialize_from<__D>(&mut self, __deserializer: __D) -> _serde::export::Result<(), __D::Error> + where __D: _serde::Deserializer<#delife> + { + #from_body + } + } + }); + quote! { #[automatically_derived] impl #de_impl_generics _serde::Deserialize<#delife> for #ident #ty_generics #where_clause { fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result where __D: _serde::Deserializer<#delife> { - #body + #main_body } + + #from_impl } } }; @@ -245,6 +260,30 @@ fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment { } } +#[cfg(feature = "deserialize_from")] +fn deserialize_from_body(cont: &Container, params: &Parameters) -> Option { + if let (None, attr::Identifier::No) = (cont.attrs.from_type(), cont.attrs.identifier()) { + match cont.body { + Body::Enum(_) => None, + Body::Struct(Style::Struct, ref fields) => { + deserialize_from_struct(None, params, fields, &cont.attrs, None, Untagged::No) + } + Body::Struct(Style::Tuple, ref fields) | + Body::Struct(Style::Newtype, ref fields) => { + deserialize_from_tuple(None, params, fields, &cont.attrs, None) + } + Body::Struct(Style::Unit, _) => None, + } + } else { + None + } +} + +#[cfg(not(feature = "deserialize_from"))] +fn deserialize_from_body(_cont: &Container, _params: &Parameters) -> Option { + None +} + fn deserialize_from(from_type: &syn::Ty) -> Fragment { quote_block! { _serde::export::Result::map( @@ -376,6 +415,110 @@ fn deserialize_tuple( } } +#[cfg(feature = "deserialize_from")] +fn deserialize_from_tuple( + variant_ident: Option<&syn::Ident>, + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, + deserializer: Option, +) -> Option { + let this = ¶ms.this; + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params,); + let delife = params.borrowed.de_lifetime(); + + // If there are getters (implying private fields), construct the local type + // and use an `Into` conversion to get the remote type. If there are no + // getters then construct the target type directly. + let construct = if params.has_getter { + let local = ¶ms.local; + quote!(#local) + } else { + quote!(#this) + }; + + let is_enum = variant_ident.is_some(); + let type_path = match variant_ident { + Some(variant_ident) => quote!(#construct::#variant_ident), + None => construct, + }; + let expecting = match variant_ident { + Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident), + None => format!("tuple struct {}", params.type_name()), + }; + + let nfields = fields.len(); + + let visit_newtype_struct = if !is_enum && nfields == 1 { + Some(deserialize_from_newtype_struct(&type_path, params, &fields[0])) + } else { + None + }; + + let visit_seq = Stmts(deserialize_from_seq(params, fields, cattrs)); + + let visitor_expr = quote! { + __Visitor { + dest: self, + lifetime: _serde::export::PhantomData, + } + }; + + let dispatch = if let Some(deserializer) = deserializer { + quote!(_serde::Deserializer::deserialize_tuple(#deserializer, #nfields, #visitor_expr)) + } else if is_enum { + quote!(_serde::de::VariantAccess::tuple_variant(__variant, #nfields, #visitor_expr)) + } else if nfields == 1 { + let type_name = cattrs.name().deserialize_name(); + quote!(_serde::Deserializer::deserialize_newtype_struct(__deserializer, #type_name, #visitor_expr)) + } else { + let type_name = cattrs.name().deserialize_name(); + quote!(_serde::Deserializer::deserialize_tuple_struct(__deserializer, #type_name, #nfields, #visitor_expr)) + }; + + let all_skipped = fields + .iter() + .all(|field| field.attrs.skip_deserializing()); + let visitor_var = if all_skipped { + quote!(_) + } else { + quote!(mut __seq) + }; + + if params.has_getter { + None + } else { + let de_from_impl_generics = de_impl_generics.with_dest(); + let de_from_ty_generics = de_ty_generics.with_dest(); + let dest_life = dest_lifetime(); + Some(quote_block! { + struct __Visitor #de_from_impl_generics #where_clause { + dest: &#dest_life mut #this #ty_generics, + lifetime: _serde::export::PhantomData<&#delife ()>, + } + + impl #de_from_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_from_ty_generics #where_clause { + type Value = (); + + fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result { + _serde::export::Formatter::write_str(formatter, #expecting) + } + + #visit_newtype_struct + + #[inline] + fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result + where __A: _serde::de::SeqAccess<#delife> + { + #visit_seq + } + } + + #dispatch + }) + } +} + fn deserialize_seq( type_path: &Tokens, params: &Parameters, @@ -453,6 +596,70 @@ fn deserialize_seq( } } +#[cfg(feature = "deserialize_from")] +fn deserialize_from_seq( + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, +) -> Fragment { + let vars = (0..fields.len()).map(field_i as fn(_) -> _); + + let deserialized_count = fields + .iter() + .filter(|field| !field.attrs.skip_deserializing()) + .count(); + let expecting = format!("tuple of {} elements", deserialized_count); + + let mut index_in_seq = 0usize; + let write_values = vars.clone().zip(fields).enumerate() + .map(|(field_index, (_, field))| { + // If there's no field name, assume we're a tuple-struct and use a numeric index + let field_name = field.ident.clone() + .unwrap_or_else(|| Ident::new(field_index.to_string())); + + if field.attrs.skip_deserializing() { + let default = Expr(expr_is_missing(&field, cattrs)); + quote! { + self.dest.#field_name = #default; + } + } else { + let handle_none = quote! { + if visit.is_none() { + return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting)); + } + }; + let write = match field.attrs.deserialize_with() { + None => { + quote! { + let visit = try!(_serde::de::SeqAccess::next_element_seed(&mut __seq, + _serde::private::de::DeserializeFromSeed(&mut self.dest.#field_name))); + #handle_none + } + } + Some(path) => { + let (wrapper, wrapper_ty) = wrap_deserialize_field_with( + params, field.ty, path); + quote!({ + #wrapper + let visit = _serde::export::Option::map( + try!(_serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)), + |__wrap| __wrap.value); + #handle_none + self.dest.#field_name = visit.unwrap(); + }) + } + }; + index_in_seq += 1; + write + } + }); + + quote_block! { + #(#write_values)* + _serde::export::Ok(()) + } +} + fn deserialize_newtype_struct(type_path: &Tokens, params: &Parameters, field: &Field) -> Tokens { let delife = params.borrowed.de_lifetime(); @@ -490,6 +697,63 @@ fn deserialize_newtype_struct(type_path: &Tokens, params: &Parameters, field: &F } } +#[cfg(feature = "deserialize_from")] +fn deserialize_from_newtype_struct( + type_path: &Tokens, + params: &Parameters, + field: &Field +) -> Tokens { + let delife = params.borrowed.de_lifetime(); + + // FIXME: can we reject this condition earlier so we don't have to handle it? + // If there's conversions that we need to do, we can't do this properly. + if field.attrs.deserialize_with().is_some() || params.has_getter { + let value = match field.attrs.deserialize_with() { + None => { + let field_ty = &field.ty; + quote! { + try!(<#field_ty as _serde::Deserialize>::deserialize(__e)) + } + } + Some(path) => { + let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); + quote!({ + #wrapper + try!(<#wrapper_ty as _serde::Deserialize>::deserialize(__e)).value + }) + } + }; + + let mut result = quote!(#type_path(#value)); + if params.has_getter { + let this = ¶ms.this; + result = quote! { + _serde::export::Into::<#this>::into(#result) + }; + } + + quote! { + #[inline] + fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result + where __E: _serde::Deserializer<#delife> + { + *self.dest = #result; + _serde::export::Ok(()) + } + } + } else { + // No conversions, just recurse on the field. + quote! { + #[inline] + fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result + where __E: _serde::Deserializer<#delife> + { + _serde::Deserialize::deserialize_from(&mut self.dest.0, __e) + } + } + } +} + enum Untagged { Yes, No, @@ -612,6 +876,121 @@ fn deserialize_struct( } } +#[cfg(feature = "deserialize_from")] +fn deserialize_from_struct( + variant_ident: Option<&syn::Ident>, + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, + deserializer: Option, + untagged: Untagged, +) -> Option { + let is_enum = variant_ident.is_some(); + + let this = ¶ms.this; + let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params,); + let delife = params.borrowed.de_lifetime(); + + let expecting = match variant_ident { + Some(variant_ident) => format!("struct variant {}::{}", params.type_name(), variant_ident), + None => format!("struct {}", params.type_name()), + }; + + let visit_seq = Stmts(deserialize_from_seq(params, fields, cattrs)); + + let (field_visitor, fields_stmt, visit_map) = + deserialize_from_struct_visitor(params, fields, cattrs); + let field_visitor = Stmts(field_visitor); + let fields_stmt = Stmts(fields_stmt); + let visit_map = Stmts(visit_map); + + let visitor_expr = quote! { + __Visitor { + dest: self, + lifetime: _serde::export::PhantomData, + } + }; + let dispatch = if let Some(deserializer) = deserializer { + quote! { + _serde::Deserializer::deserialize_any(#deserializer, #visitor_expr) + } + } else if is_enum { + quote! { + _serde::de::VariantAccess::struct_variant(__variant, FIELDS, #visitor_expr) + } + } else { + let type_name = cattrs.name().deserialize_name(); + quote! { + _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, #visitor_expr) + } + }; + + + let all_skipped = fields + .iter() + .all(|field| field.attrs.skip_deserializing()); + let visitor_var = if all_skipped { + quote!(_) + } else { + quote!(mut __seq) + }; + + // untagged struct variants do not get a visit_seq method + let visit_seq = match untagged { + Untagged::Yes => None, + Untagged::No => { + Some(quote! { + #[inline] + fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result + where __A: _serde::de::SeqAccess<#delife> + { + #visit_seq + } + }) + + } + }; + + if params.has_getter { + None + } else { + let de_from_impl_generics = de_impl_generics.with_dest(); + let de_from_ty_generics = de_ty_generics.with_dest(); + let dest_life = dest_lifetime(); + + Some(quote_block! { + #field_visitor + + struct __Visitor #de_from_impl_generics #where_clause { + dest: &#dest_life mut #this #ty_generics, + lifetime: _serde::export::PhantomData<&#delife ()>, + } + + impl #de_from_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_from_ty_generics #where_clause { + type Value = (); + + fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result { + _serde::export::Formatter::write_str(formatter, #expecting) + } + + #visit_seq + + #[inline] + #[allow(unreachable_code)] + fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result + where __A: _serde::de::MapAccess<#delife> + { + #visit_map + } + } + + #fields_stmt + + #dispatch + }) + } +} + fn deserialize_enum( params: &Parameters, variants: &[Variant], @@ -1740,6 +2119,176 @@ fn deserialize_map( } } +#[cfg(feature = "deserialize_from")] +fn deserialize_from_struct_visitor( + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, +) -> (Fragment, Fragment, Fragment) { + let field_names_idents: Vec<_> = fields + .iter() + .enumerate() + .filter(|&(_, field)| !field.attrs.skip_deserializing()) + .map(|(i, field)| (field.attrs.name().deserialize_name(), field_i(i)),) + .collect(); + + let fields_stmt = { + let field_names = field_names_idents.iter().map(|&(ref name, _)| name); + quote_block! { + const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; + } + }; + + let field_visitor = deserialize_generated_identifier(field_names_idents, cattrs, false); + + let visit_map = deserialize_from_map(params, fields, cattrs); + + (field_visitor, fields_stmt, visit_map) +} + +#[cfg(feature = "deserialize_from")] +fn deserialize_from_map( + params: &Parameters, + fields: &[Field], + cattrs: &attr::Container, +) -> Fragment { + // Create the field names for the fields. + let fields_names: Vec<_> = fields + .iter() + .enumerate() + .map(|(i, field)| (field, field_i(i))) + .collect(); + + // For deserialize_from, declare booleans for each field that will be deserialized. + let let_flags = fields_names + .iter() + .filter(|&&(field, _)| !field.attrs.skip_deserializing()) + .map( + |&(_, ref name)| { + quote! { + let mut #name: bool = false; + } + }, + ); + + // Match arms to extract a value for a field. + let value_arms_from = fields_names.iter() + .filter(|&&(field, _)| !field.attrs.skip_deserializing()) + .map(|&(field, ref name)| { + let deser_name = field.attrs.name().deserialize_name(); + let field_name = &field.ident; + + let visit = match field.attrs.deserialize_with() { + None => { + quote! { + try!(_serde::de::MapAccess::next_value_seed(&mut __map, _serde::private::de::DeserializeFromSeed(&mut self.dest.#field_name))) + } + } + Some(path) => { + let (wrapper, wrapper_ty) = wrap_deserialize_field_with( + params, field.ty, path); + quote!({ + #wrapper + self.dest.#field_name = try!(_serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map)).value + }) + } + }; + quote! { + __Field::#name => { + if #name { + return _serde::export::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name)); + } + #visit; + #name = true; + } + } + }); + + // Visit ignored values to consume them + let ignored_arm = if cattrs.deny_unknown_fields() { + None + } else { + Some(quote! { + _ => { let _ = try!(_serde::de::MapAccess::next_value::<_serde::de::IgnoredAny>(&mut __map)); } + }) + }; + + let all_skipped = fields + .iter() + .all(|field| field.attrs.skip_deserializing()); + + let match_keys = if cattrs.deny_unknown_fields() && all_skipped { + quote! { + // FIXME: Once we drop support for Rust 1.15: + // let _serde::export::None::<__Field> = try!(_serde::de::MapAccess::next_key(&mut __map)); + _serde::export::Option::map( + try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)), + |__impossible| match __impossible {}); + } + } else { + quote! { + while let _serde::export::Some(__key) = try!(_serde::de::MapAccess::next_key::<__Field>(&mut __map)) { + match __key { + #(#value_arms_from)* + #ignored_arm + } + } + } + }; + + let check_flags = fields_names + .iter() + .filter(|&&(field, _)| !field.attrs.skip_deserializing()) + .map( + |&(field, ref name)| { + let missing_expr = Expr(expr_is_missing(&field, cattrs)); + let field_name = &field.ident; + quote! { + if !#name { + self.dest.#field_name = #missing_expr; + }; + } + }, + ); + + let this = ¶ms.this; + let (_, _, ty_generics, _) = split_with_de_lifetime(params,); + + let let_default = match *cattrs.default() { + attr::Default::Default => { + Some( + quote!( + let __default: #this #ty_generics = _serde::export::Default::default(); + ), + ) + } + attr::Default::Path(ref path) => { + Some( + quote!( + let __default: #this #ty_generics = #path(); + ), + ) + } + attr::Default::None => { + // We don't need the default value, to prevent an unused variable warning + // we'll leave the line empty. + None + } + }; + + quote_block! { + #(#let_flags)* + + #match_keys + + #let_default + + #(#check_flags)* + + _serde::export::Ok(()) + } +} + fn field_i(i: usize) -> Ident { Ident::new(format!("__field{}", i)) } @@ -1878,6 +2427,8 @@ fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment { } struct DeImplGenerics<'a>(&'a Parameters); +#[cfg(feature = "deserialize_from")] +struct DeFromImplGenerics<'a>(&'a Parameters); impl<'a> ToTokens for DeImplGenerics<'a> { fn to_tokens(&self, tokens: &mut Tokens) { @@ -1890,7 +2441,38 @@ impl<'a> ToTokens for DeImplGenerics<'a> { } } +#[cfg(feature = "deserialize_from")] +impl<'a> ToTokens for DeFromImplGenerics<'a> { + fn to_tokens(&self, tokens: &mut Tokens) { + let dest_lifetime = dest_lifetime(); + let mut generics = self.0.generics.clone(); + + // Add lifetime for `&'dest mut Self, and `'a: 'dest` + for lifetime in &mut generics.lifetimes { + lifetime.bounds.push(dest_lifetime.lifetime.clone()); + } + for generic in &mut generics.ty_params { + generic.bounds.push(syn::TyParamBound::Region(dest_lifetime.lifetime.clone())); + } + generics.lifetimes.insert(0, dest_lifetime); + if let Some(de_lifetime) = self.0.borrowed.de_lifetime_def() { + generics.lifetimes.insert(0, de_lifetime); + } + let (impl_generics, _, _) = generics.split_for_impl(); + impl_generics.to_tokens(tokens); + } +} + +#[cfg(feature = "deserialize_from")] +impl<'a> DeImplGenerics<'a> { + fn with_dest(self) -> DeFromImplGenerics<'a> { + DeFromImplGenerics(self.0) + } +} + struct DeTyGenerics<'a>(&'a Parameters); +#[cfg(feature = "deserialize_from")] +struct DeFromTyGenerics<'a>(&'a Parameters); impl<'a> ToTokens for DeTyGenerics<'a> { fn to_tokens(&self, tokens: &mut Tokens) { @@ -1905,6 +2487,34 @@ impl<'a> ToTokens for DeTyGenerics<'a> { } } +#[cfg(feature = "deserialize_from")] +impl<'a> ToTokens for DeFromTyGenerics<'a> { + fn to_tokens(&self, tokens: &mut Tokens) { + let mut generics = self.0.generics.clone(); + generics.lifetimes.insert(0, dest_lifetime()); + + if self.0.borrowed.de_lifetime_def().is_some() { + generics + .lifetimes + .insert(0, syn::LifetimeDef::new("'de")); + } + let (_, ty_generics, _) = generics.split_for_impl(); + ty_generics.to_tokens(tokens); + } +} + +#[cfg(feature = "deserialize_from")] +impl<'a> DeTyGenerics<'a> { + fn with_dest(self) -> DeFromTyGenerics<'a> { + DeFromTyGenerics(self.0) + } +} + +#[cfg(feature = "deserialize_from")] +fn dest_lifetime() -> syn::LifetimeDef { + syn::LifetimeDef::new("'dest") +} + fn split_with_de_lifetime(params: &Parameters,) -> (DeImplGenerics, DeTyGenerics, syn::TyGenerics, &syn::WhereClause) { let de_impl_generics = DeImplGenerics(¶ms); From 34936be574649459218a92ebd412fc91ec7adac1 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Mon, 4 Dec 2017 12:42:43 -0500 Subject: [PATCH 04/17] test deserialize_from --- serde_test/Cargo.toml | 2 +- serde_test/src/assert.rs | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/serde_test/Cargo.toml b/serde_test/Cargo.toml index 63de886c..55461bed 100644 --- a/serde_test/Cargo.toml +++ b/serde_test/Cargo.toml @@ -16,7 +16,7 @@ serde = { version = "1.0.16", path = "../serde" } [dev-dependencies] serde = { version = "1.0.16", path = "../serde", features = ["rc"] } -serde_derive = { version = "1.0", path = "../serde_derive" } +serde_derive = { version = "1.0", path = "../serde_derive", features = ["deserialize_from"]} [badges] travis-ci = { repository = "serde-rs/serde" } diff --git a/serde_test/src/assert.rs b/serde_test/src/assert.rs index 72116b71..903a5616 100644 --- a/serde_test/src/assert.rs +++ b/serde_test/src/assert.rs @@ -184,11 +184,27 @@ where T: Deserialize<'de> + PartialEq + Debug, { let mut de = Deserializer::new(tokens); - match T::deserialize(&mut de) { - Ok(v) => assert_eq!(v, *value), + let mut deserialized_val = match T::deserialize(&mut de) { + Ok(v) => { + assert_eq!(v, *value); + v + } Err(e) => panic!("tokens failed to deserialize: {}", e), + }; + if de.remaining() > 0 { + panic!("{} remaining tokens", de.remaining()); } + // Do the same thing for deserialize_from. This isn't *great* because a no-op + // impl of deserialize_from can technically succeed here. Still, this should + // catch a lot of junk. + let mut de = Deserializer::new(tokens); + match deserialized_val.deserialize_from(&mut de) { + Ok(()) => { + assert_eq!(deserialized_val, *value); + } + Err(e) => panic!("tokens failed to deserialize_from: {}", e), + } if de.remaining() > 0 { panic!("{} remaining tokens", de.remaining()); } From ffd2017c6ff7fc2b971207d0b4b0bac6acd123d4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 21:27:42 -0800 Subject: [PATCH 05/17] Use the default deserialize_from for Option The custom one was functionally identical to the default implementation given by the Deserialize trait. If someone has benchmarks that the custom one performs better, we can put it back. --- serde/src/de/impls.rs | 55 ++++--------------------------------------- 1 file changed, 5 insertions(+), 50 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index c7ea2ede..494540b0 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -485,7 +485,6 @@ forwarded_impl!((), Box, CString::into_boxed_c_str); struct OptionVisitor { marker: PhantomData, } -struct OptionFromVisitor<'a, T: 'a>(&'a mut Option); impl<'de, T> Visitor<'de> for OptionVisitor where @@ -522,49 +521,6 @@ where } } -impl<'a, 'de, T> Visitor<'de> for OptionFromVisitor<'a, T> -where - T: Deserialize<'de>, -{ - type Value = (); - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("option") - } - - #[inline] - fn visit_unit(self) -> Result<(), E> - where - E: Error, - { - *self.0 = None; - Ok(()) - } - - #[inline] - fn visit_none(self) -> Result<(), E> - where - E: Error, - { - *self.0 = None; - Ok(()) - } - - #[inline] - fn visit_some(self, deserializer: D) -> Result<(), D::Error> - where - D: Deserializer<'de>, - { - // The some enum's repr is opaque, so we can't play cute tricks with - // its tag to build this in place unconditionally. - // - // FIXME: investigate whether branching on the old value being Some to - // deserialize_from the value is profitable (probably data-dependent?) - *self.0 = try!(T::deserialize(deserializer).map(Some)); - Ok(()) - } -} - impl<'de, T> Deserialize<'de> for Option where T: Deserialize<'de>, @@ -576,12 +532,11 @@ where deserializer.deserialize_option(OptionVisitor { marker: PhantomData }) } - fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error> - where - D: Deserializer<'de>, - { - deserializer.deserialize_option(OptionFromVisitor(self)) - } + // The Some variant's repr is opaque, so we can't play cute tricks with its + // tag to have deserialize_from build the content in place unconditionally. + // + // FIXME: investigate whether branching on the old value being Some to + // deserialize_from the value is profitable (probably data-dependent?) } //////////////////////////////////////////////////////////////////////////////// From 9641978481cfbeada33155318270734834201cbd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 21:55:07 -0800 Subject: [PATCH 06/17] Hide deserialize_from --- serde/src/de/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index aa1cc3af..219907bd 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -520,12 +520,11 @@ pub trait Deserialize<'de>: Sized { /// If you manually implement this, your recursive deserializations should /// use `deserialize_from`. /// - /// TODO: example - /// - /// ``` - /// // Something with a loop that returns on error. - /// - /// ``` + /// This method is stable and an official public API, but hidden from the + /// documentation because it is almost never what newbies are looking for. + /// Showing it in rustdoc would cause it to be featured more prominently + /// than it deserves. + #[doc(hidden)] fn deserialize_from(&mut self, deserializer: D) -> Result<(), D::Error> where D: Deserializer<'de> { From a45f1ae9150aa83a1734c204a6b665f546990f6f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 22:02:11 -0800 Subject: [PATCH 07/17] Remove unused dev-dependency of serde_test on deserialize_from --- serde_test/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serde_test/Cargo.toml b/serde_test/Cargo.toml index 55461bed..63de886c 100644 --- a/serde_test/Cargo.toml +++ b/serde_test/Cargo.toml @@ -16,7 +16,7 @@ serde = { version = "1.0.16", path = "../serde" } [dev-dependencies] serde = { version = "1.0.16", path = "../serde", features = ["rc"] } -serde_derive = { version = "1.0", path = "../serde_derive", features = ["deserialize_from"]} +serde_derive = { version = "1.0", path = "../serde_derive" } [badges] travis-ci = { repository = "serde-rs/serde" } From a93f2ebff0b6e2e0facd2d0df215d1978987955f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 22:46:25 -0800 Subject: [PATCH 08/17] Enable deserialize_from in the test suite --- test_suite/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_suite/Cargo.toml b/test_suite/Cargo.toml index 74c4a7b5..1bd4a99c 100644 --- a/test_suite/Cargo.toml +++ b/test_suite/Cargo.toml @@ -11,7 +11,7 @@ unstable = ["serde/unstable", "compiletest_rs"] fnv = "1.0" rustc-serialize = "0.3.16" serde = { path = "../serde", features = ["rc"] } -serde_derive = { path = "../serde_derive" } +serde_derive = { path = "../serde_derive", features = ["deserialize_from"] } serde_test = { path = "../serde_test" } [dependencies] From 61ca928325dae76ceca7706efd870f0bb38da7d7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 22:46:46 -0800 Subject: [PATCH 09/17] Can never see getters in a deserialize_from --- serde_derive/src/de.rs | 157 +++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 93 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 04b0929a..a41182ec 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -262,15 +262,19 @@ fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment { #[cfg(feature = "deserialize_from")] fn deserialize_from_body(cont: &Container, params: &Parameters) -> Option { + // Only remote derives have getters, and we do not generate deserialize_from + // for remote derives. + assert!(!params.has_getter); + if let (None, attr::Identifier::No) = (cont.attrs.from_type(), cont.attrs.identifier()) { match cont.body { Body::Enum(_) => None, Body::Struct(Style::Struct, ref fields) => { - deserialize_from_struct(None, params, fields, &cont.attrs, None, Untagged::No) + Some(deserialize_from_struct(None, params, fields, &cont.attrs, None, Untagged::No)) } Body::Struct(Style::Tuple, ref fields) | Body::Struct(Style::Newtype, ref fields) => { - deserialize_from_tuple(None, params, fields, &cont.attrs, None) + Some(deserialize_from_tuple(None, params, fields, &cont.attrs, None)) } Body::Struct(Style::Unit, _) => None, } @@ -422,25 +426,15 @@ fn deserialize_from_tuple( fields: &[Field], cattrs: &attr::Container, deserializer: Option, -) -> Option { +) -> Fragment { let this = ¶ms.this; let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params,); let delife = params.borrowed.de_lifetime(); - // If there are getters (implying private fields), construct the local type - // and use an `Into` conversion to get the remote type. If there are no - // getters then construct the target type directly. - let construct = if params.has_getter { - let local = ¶ms.local; - quote!(#local) - } else { - quote!(#this) - }; - let is_enum = variant_ident.is_some(); let type_path = match variant_ident { - Some(variant_ident) => quote!(#construct::#variant_ident), - None => construct, + Some(variant_ident) => quote!(#this::#variant_ident), + None => quote!(#this), }; let expecting = match variant_ident { Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident), @@ -485,37 +479,34 @@ fn deserialize_from_tuple( quote!(mut __seq) }; - if params.has_getter { - None - } else { - let de_from_impl_generics = de_impl_generics.with_dest(); - let de_from_ty_generics = de_ty_generics.with_dest(); - let dest_life = dest_lifetime(); - Some(quote_block! { - struct __Visitor #de_from_impl_generics #where_clause { - dest: &#dest_life mut #this #ty_generics, - lifetime: _serde::export::PhantomData<&#delife ()>, + let de_from_impl_generics = de_impl_generics.with_dest(); + let de_from_ty_generics = de_ty_generics.with_dest(); + let dest_life = dest_lifetime(); + + quote_block! { + struct __Visitor #de_from_impl_generics #where_clause { + dest: &#dest_life mut #this #ty_generics, + lifetime: _serde::export::PhantomData<&#delife ()>, + } + + impl #de_from_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_from_ty_generics #where_clause { + type Value = (); + + fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result { + _serde::export::Formatter::write_str(formatter, #expecting) } - impl #de_from_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_from_ty_generics #where_clause { - type Value = (); + #visit_newtype_struct - fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result { - _serde::export::Formatter::write_str(formatter, #expecting) - } - - #visit_newtype_struct - - #[inline] - fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result - where __A: _serde::de::SeqAccess<#delife> - { - #visit_seq - } + #[inline] + fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result + where __A: _serde::de::SeqAccess<#delife> + { + #visit_seq } + } - #dispatch - }) + #dispatch } } @@ -707,30 +698,14 @@ fn deserialize_from_newtype_struct( // FIXME: can we reject this condition earlier so we don't have to handle it? // If there's conversions that we need to do, we can't do this properly. - if field.attrs.deserialize_with().is_some() || params.has_getter { - let value = match field.attrs.deserialize_with() { - None => { - let field_ty = &field.ty; - quote! { - try!(<#field_ty as _serde::Deserialize>::deserialize(__e)) - } - } - Some(path) => { - let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); - quote!({ - #wrapper - try!(<#wrapper_ty as _serde::Deserialize>::deserialize(__e)).value - }) - } - }; + if let Some(path) = field.attrs.deserialize_with() { + let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); + let value = quote!({ + #wrapper + try!(<#wrapper_ty as _serde::Deserialize>::deserialize(__e)).value + }); let mut result = quote!(#type_path(#value)); - if params.has_getter { - let this = ¶ms.this; - result = quote! { - _serde::export::Into::<#this>::into(#result) - }; - } quote! { #[inline] @@ -884,7 +859,7 @@ fn deserialize_from_struct( cattrs: &attr::Container, deserializer: Option, untagged: Untagged, -) -> Option { +) -> Fragment { let is_enum = variant_ident.is_some(); let this = ¶ms.this; @@ -951,43 +926,39 @@ fn deserialize_from_struct( } }; - if params.has_getter { - None - } else { - let de_from_impl_generics = de_impl_generics.with_dest(); - let de_from_ty_generics = de_ty_generics.with_dest(); - let dest_life = dest_lifetime(); + let de_from_impl_generics = de_impl_generics.with_dest(); + let de_from_ty_generics = de_ty_generics.with_dest(); + let dest_life = dest_lifetime(); - Some(quote_block! { - #field_visitor + quote_block! { + #field_visitor - struct __Visitor #de_from_impl_generics #where_clause { - dest: &#dest_life mut #this #ty_generics, - lifetime: _serde::export::PhantomData<&#delife ()>, + struct __Visitor #de_from_impl_generics #where_clause { + dest: &#dest_life mut #this #ty_generics, + lifetime: _serde::export::PhantomData<&#delife ()>, + } + + impl #de_from_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_from_ty_generics #where_clause { + type Value = (); + + fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result { + _serde::export::Formatter::write_str(formatter, #expecting) } - impl #de_from_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_from_ty_generics #where_clause { - type Value = (); + #visit_seq - fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result { - _serde::export::Formatter::write_str(formatter, #expecting) - } - - #visit_seq - - #[inline] - #[allow(unreachable_code)] - fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result - where __A: _serde::de::MapAccess<#delife> - { - #visit_map - } + #[inline] + #[allow(unreachable_code)] + fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result + where __A: _serde::de::MapAccess<#delife> + { + #visit_map } + } - #fields_stmt + #fields_stmt - #dispatch - }) + #dispatch } } From ccae35d92a72e52f2052d913620a0dc5be726aba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 22:55:28 -0800 Subject: [PATCH 10/17] Do not emit deserialize_from if every field has deserialize_with --- serde_derive/src/de.rs | 49 ++++++++++++------------------------------ 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index a41182ec..7cb1f26e 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -266,6 +266,10 @@ fn deserialize_from_body(cont: &Container, params: &Parameters) -> Option None, @@ -432,10 +436,6 @@ fn deserialize_from_tuple( let delife = params.borrowed.de_lifetime(); let is_enum = variant_ident.is_some(); - let type_path = match variant_ident { - Some(variant_ident) => quote!(#this::#variant_ident), - None => quote!(#this), - }; let expecting = match variant_ident { Some(variant_ident) => format!("tuple variant {}::{}", params.type_name(), variant_ident), None => format!("tuple struct {}", params.type_name()), @@ -444,7 +444,7 @@ fn deserialize_from_tuple( let nfields = fields.len(); let visit_newtype_struct = if !is_enum && nfields == 1 { - Some(deserialize_from_newtype_struct(&type_path, params, &fields[0])) + Some(deserialize_from_newtype_struct(params, &fields[0])) } else { None }; @@ -690,41 +690,20 @@ fn deserialize_newtype_struct(type_path: &Tokens, params: &Parameters, field: &F #[cfg(feature = "deserialize_from")] fn deserialize_from_newtype_struct( - type_path: &Tokens, params: &Parameters, field: &Field ) -> Tokens { + // We do not generate deserialize_from if every field has a deserialize_with. + assert!(field.attrs.deserialize_with().is_none()); + let delife = params.borrowed.de_lifetime(); - // FIXME: can we reject this condition earlier so we don't have to handle it? - // If there's conversions that we need to do, we can't do this properly. - if let Some(path) = field.attrs.deserialize_with() { - let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path); - let value = quote!({ - #wrapper - try!(<#wrapper_ty as _serde::Deserialize>::deserialize(__e)).value - }); - - let mut result = quote!(#type_path(#value)); - - quote! { - #[inline] - fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result - where __E: _serde::Deserializer<#delife> - { - *self.dest = #result; - _serde::export::Ok(()) - } - } - } else { - // No conversions, just recurse on the field. - quote! { - #[inline] - fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result - where __E: _serde::Deserializer<#delife> - { - _serde::Deserialize::deserialize_from(&mut self.dest.0, __e) - } + quote! { + #[inline] + fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result + where __E: _serde::Deserializer<#delife> + { + _serde::Deserialize::deserialize_from(&mut self.dest.0, __e) } } } From 85e3ddc2b88d31109063492ca0f6a9c07651525e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 23:04:44 -0800 Subject: [PATCH 11/17] Less indentiation in deserialize_from_body --- serde_derive/src/de.rs | 27 +++++++++++++-------------- serde_derive_internals/src/attr.rs | 9 +++++++++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 7cb1f26e..58d97c63 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -266,24 +266,23 @@ fn deserialize_from_body(cont: &Container, params: &Parameters) -> Option None, - Body::Struct(Style::Struct, ref fields) => { - Some(deserialize_from_struct(None, params, fields, &cont.attrs, None, Untagged::No)) - } - Body::Struct(Style::Tuple, ref fields) | - Body::Struct(Style::Newtype, ref fields) => { - Some(deserialize_from_tuple(None, params, fields, &cont.attrs, None)) - } - Body::Struct(Style::Unit, _) => None, + match cont.body { + Body::Enum(_) => None, + Body::Struct(Style::Struct, ref fields) => { + Some(deserialize_from_struct(None, params, fields, &cont.attrs, None, Untagged::No)) } - } else { - None + Body::Struct(Style::Tuple, ref fields) | + Body::Struct(Style::Newtype, ref fields) => { + Some(deserialize_from_tuple(None, params, fields, &cont.attrs, None)) + } + Body::Struct(Style::Unit, _) => None, } } diff --git a/serde_derive_internals/src/attr.rs b/serde_derive_internals/src/attr.rs index 7baeb59b..3cf7e74d 100644 --- a/serde_derive_internals/src/attr.rs +++ b/serde_derive_internals/src/attr.rs @@ -164,6 +164,15 @@ pub enum Identifier { Variant, } +impl Identifier { + pub fn is_some(self) -> bool { + match self { + Identifier::No => false, + Identifier::Field | Identifier::Variant => true, + } + } +} + impl Container { /// Extract out the `#[serde(...)]` attributes from an item. pub fn from_ast(cx: &Ctxt, item: &syn::DeriveInput) -> Self { From d9e894911f94f6935fe2697252ba0a868aa90924 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 23:12:54 -0800 Subject: [PATCH 12/17] Move all the deserialize_from derive code behind flag --- serde_derive/src/de.rs | 45 ++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 58d97c63..361caad0 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -40,18 +40,7 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result(&mut self, __deserializer: __D) -> _serde::export::Result<(), __D::Error> - where __D: _serde::Deserializer<#delife> - { - #from_body - } - } - }); + let fn_deserialize_from = deserialize_from_body(&cont, ¶ms); quote! { #[automatically_derived] @@ -62,7 +51,7 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result Fragment { } #[cfg(feature = "deserialize_from")] -fn deserialize_from_body(cont: &Container, params: &Parameters) -> Option { +fn deserialize_from_body(cont: &Container, params: &Parameters) -> Option { // Only remote derives have getters, and we do not generate deserialize_from // for remote derives. assert!(!params.has_getter); @@ -273,21 +262,35 @@ fn deserialize_from_body(cont: &Container, params: &Parameters) -> Option None, + let code = match cont.body { Body::Struct(Style::Struct, ref fields) => { - Some(deserialize_from_struct(None, params, fields, &cont.attrs, None, Untagged::No)) + deserialize_from_struct(None, params, fields, &cont.attrs, None, Untagged::No) } Body::Struct(Style::Tuple, ref fields) | Body::Struct(Style::Newtype, ref fields) => { - Some(deserialize_from_tuple(None, params, fields, &cont.attrs, None)) + deserialize_from_tuple(None, params, fields, &cont.attrs, None) } - Body::Struct(Style::Unit, _) => None, - } + Body::Enum(_) | Body::Struct(Style::Unit, _) => { + return None; + } + }; + + let delife = params.borrowed.de_lifetime(); + let stmts = Stmts(code); + + let fn_deserialize_from = quote_block! { + fn deserialize_from<__D>(&mut self, __deserializer: __D) -> _serde::export::Result<(), __D::Error> + where __D: _serde::Deserializer<#delife> + { + #stmts + } + }; + + Some(Stmts(fn_deserialize_from)) } #[cfg(not(feature = "deserialize_from"))] -fn deserialize_from_body(_cont: &Container, _params: &Parameters) -> Option { +fn deserialize_from_body(_cont: &Container, _params: &Parameters) -> Option { None } From 3700779bfa1549d71db95b55d6402623c60f3922 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 23:18:08 -0800 Subject: [PATCH 13/17] More meaningful names and types for nop_reserve --- serde/src/de/impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 494540b0..87aa6c68 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -669,7 +669,7 @@ macro_rules! seq_impl { // Dummy impl of reserve #[cfg(any(feature = "std", feature = "alloc"))] -fn nop_reserve(_x: T, _y: U) { } +fn nop_reserve(_seq: T, _n: usize) {} #[cfg(any(feature = "std", feature = "alloc"))] seq_impl!( From 9360094ba777be0a2c429dbb24fcd8b1ad6414cf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 10 Dec 2017 23:19:25 -0800 Subject: [PATCH 14/17] Revert main_body naming change The naming here isn't great but no need to change it in this PR. --- serde_derive/src/de.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 361caad0..8b3ac944 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -25,7 +25,7 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result Result(__deserializer: __D) -> _serde::export::Result<#remote #ty_generics, __D::Error> where __D: _serde::Deserializer<#delife> { - #main_body + #body } } } @@ -48,7 +48,7 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result(__deserializer: __D) -> _serde::export::Result where __D: _serde::Deserializer<#delife> { - #main_body + #body } #fn_deserialize_from From 65104aca9cddef98155fe2e2e284077f5cd8e95b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 11 Dec 2017 17:55:23 -0800 Subject: [PATCH 15/17] Remove need for allow(unreachable_code) --- serde_derive/src/de.rs | 26 +++++++++++++++++++------- serde_derive/src/fragment.rs | 9 +++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 8b3ac944..e612c0ac 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -929,7 +929,6 @@ fn deserialize_from_struct( #visit_seq #[inline] - #[allow(unreachable_code)] fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result where __A: _serde::de::MapAccess<#delife> { @@ -2193,12 +2192,25 @@ fn deserialize_from_map( .filter(|&&(field, _)| !field.attrs.skip_deserializing()) .map( |&(field, ref name)| { - let missing_expr = Expr(expr_is_missing(&field, cattrs)); - let field_name = &field.ident; - quote! { - if !#name { - self.dest.#field_name = #missing_expr; - }; + let missing_expr = expr_is_missing(&field, cattrs); + // If missing_expr unconditionally returns an error, don't try + // to assign its value to self.dest. Maybe this could be handled + // more elegantly. + if missing_expr.as_ref().as_str().starts_with("return ") { + let missing_expr = Stmts(missing_expr); + quote! { + if !#name { + #missing_expr; + } + } + } else { + let field_name = &field.ident; + let missing_expr = Expr(missing_expr); + quote! { + if !#name { + self.dest.#field_name = #missing_expr; + }; + } } }, ); diff --git a/serde_derive/src/fragment.rs b/serde_derive/src/fragment.rs index 58cf0a2c..c882bcf9 100644 --- a/serde_derive/src/fragment.rs +++ b/serde_derive/src/fragment.rs @@ -73,3 +73,12 @@ impl ToTokens for Match { } } } + +impl AsRef for Fragment { + fn as_ref(&self) -> &Tokens { + match *self { + Fragment::Expr(ref expr) => expr, + Fragment::Block(ref block) => block, + } + } +} From 13e7bee0e64e09a951353ad97d3ebdfac8d1c79a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 11 Dec 2017 18:13:13 -0800 Subject: [PATCH 16/17] Eliminate need for unwrap in deserialize_from_seq --- serde_derive/src/de.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index e612c0ac..b0edc267 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -616,17 +616,17 @@ fn deserialize_from_seq( self.dest.#field_name = #default; } } else { - let handle_none = quote! { - if visit.is_none() { - return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting)); - } + let return_invalid_length = quote! { + return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting)); }; let write = match field.attrs.deserialize_with() { None => { quote! { - let visit = try!(_serde::de::SeqAccess::next_element_seed(&mut __seq, - _serde::private::de::DeserializeFromSeed(&mut self.dest.#field_name))); - #handle_none + if let _serde::export::None = try!(_serde::de::SeqAccess::next_element_seed(&mut __seq, + _serde::private::de::DeserializeFromSeed(&mut self.dest.#field_name))) + { + #return_invalid_length + } } } Some(path) => { @@ -634,11 +634,14 @@ fn deserialize_from_seq( params, field.ty, path); quote!({ #wrapper - let visit = _serde::export::Option::map( - try!(_serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)), - |__wrap| __wrap.value); - #handle_none - self.dest.#field_name = visit.unwrap(); + match try!(_serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)) { + _serde::export::Some(__wrap) => { + self.dest.#field_name = __wrap.value; + } + _serde::export::None => { + #return_invalid_length + } + } }) } }; From 0dac13e4db4d2ca947fe6ba9963879d00217bce5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 11 Dec 2017 20:59:54 -0800 Subject: [PATCH 17/17] Resolve conflict with pr 1115 --- serde_derive/src/de.rs | 25 +++++++++++++++++++++++++ test_suite/tests/test_de.rs | 6 ++++++ 2 files changed, 31 insertions(+) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 6425d4e1..dbe5a193 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -673,7 +673,32 @@ fn deserialize_from_seq( } }); + let this = ¶ms.this; + let (_, ty_generics, _) = params.generics.split_for_impl(); + let let_default = match *cattrs.default() { + attr::Default::Default => { + Some( + quote!( + let __default: #this #ty_generics = _serde::export::Default::default(); + ), + ) + } + attr::Default::Path(ref path) => { + Some( + quote!( + let __default: #this #ty_generics = #path(); + ), + ) + } + attr::Default::None => { + // We don't need the default value, to prevent an unused variable warning + // we'll leave the line empty. + None + } + }; + quote_block! { + #let_default #(#write_values)* _serde::export::Ok(()) } diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index 4c801d62..58bcf464 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -86,6 +86,12 @@ struct StructSkipDefault { #[serde(skip_deserializing)] a: i32, } +#[derive(PartialEq, Debug, Deserialize)] +#[serde(default)] +struct StructSkipDefaultGeneric { + #[serde(skip_deserializing)] t: T, +} + impl Default for StructSkipDefault { fn default() -> Self { StructSkipDefault {