From f4414bfc1412b803cdfb55b4043c178a16d5c238 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 7 May 2016 12:33:59 -0700 Subject: [PATCH] Reduce dependence on type inference --- serde_codegen/src/attr.rs | 53 ++------------ serde_codegen/src/de.rs | 127 ++++++++++++++++++++++++++++++---- serde_tests/tests/test.rs.in | 1 + serde_tests/tests/test_de.rs | 2 +- serde_tests/tests/test_gen.rs | 56 +++++++++++++++ 5 files changed, 178 insertions(+), 61 deletions(-) create mode 100644 serde_tests/tests/test_gen.rs diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index 00ff5069..c9ff9e80 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -180,7 +180,7 @@ pub struct FieldAttrs { skip_serializing_field_if: Option>, default_expr_if_missing: Option>, serialize_with: Option>, - deserialize_with: P, + deserialize_with: Option, } impl FieldAttrs { @@ -197,8 +197,6 @@ impl FieldAttrs { None => { cx.span_bug(field.span, "struct field has no name?") } }; - let identity = quote_expr!(cx, |x| x); - let mut field_attrs = FieldAttrs { name: Name::new(field_ident), skip_serializing_field: false, @@ -206,7 +204,7 @@ impl FieldAttrs { skip_serializing_field_if: None, default_expr_if_missing: None, serialize_with: None, - deserialize_with: identity, + deserialize_with: None, }; for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) { @@ -287,14 +285,8 @@ impl FieldAttrs { // Parse `#[serde(deserialize_with="...")]` ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => { - let expr = wrap_deserialize_with( - cx, - &field.ty, - generics, - try!(parse_lit_into_path(cx, name, lit)), - ); - - field_attrs.deserialize_with = expr; + let path = try!(parse_lit_into_path(cx, name, lit)); + field_attrs.deserialize_with = Some(path); } _ => { @@ -348,8 +340,8 @@ impl FieldAttrs { self.serialize_with.as_ref() } - pub fn deserialize_with(&self) -> &P { - &self.deserialize_with + pub fn deserialize_with(&self) -> Option<&ast::Path> { + self.deserialize_with.as_ref() } } @@ -612,36 +604,3 @@ fn wrap_serialize_with(cx: &ExtCtxt, } }) } - -/// This function wraps the expression in `#[serde(deserialize_with="...")]` in a trait to prevent -/// it from accessing the internal `Deserialize` state. -fn wrap_deserialize_with(cx: &ExtCtxt, - field_ty: &P, - generics: &ast::Generics, - path: ast::Path) -> P { - // Quasi-quoting doesn't do a great job of expanding generics into paths, so manually build it. - let ty_path = AstBuilder::new().path() - .segment("__SerdeDeserializeWithStruct") - .with_generics(generics.clone()) - .build() - .build(); - - let where_clause = &generics.where_clause; - - quote_expr!(cx, ({ - struct __SerdeDeserializeWithStruct $generics $where_clause { - value: $field_ty, - } - - impl $generics _serde::de::Deserialize for $ty_path $where_clause { - fn deserialize(deserializer: &mut D) -> ::std::result::Result - where D: _serde::de::Deserializer - { - let value = try!($path(deserializer)); - Ok(__SerdeDeserializeWithStruct { value: value }) - } - } - - |visit: $ty_path| visit.value - })) -} diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 366fb668..f98fe765 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -509,12 +509,14 @@ fn deserialize_seq( fn deserialize_struct_as_seq( cx: &ExtCtxt, builder: &aster::AstBuilder, + type_ident: Ident, struct_path: ast::Path, + impl_generics: &ast::Generics, fields: &[(&ast::StructField, attr::FieldAttrs)], ) -> Result, Error> { let let_values: Vec<_> = fields.iter() .enumerate() - .map(|(i, &(_, ref attrs))| { + .map(|(i, &(field, ref attrs))| { let name = builder.id(format!("__field{}", i)); if attrs.skip_deserializing_field() { let default = attrs.expr_is_missing(); @@ -522,10 +524,24 @@ fn deserialize_struct_as_seq( let $name = $default; ).unwrap() } else { - let deserialize_with = attrs.deserialize_with(); + let visit = match attrs.deserialize_with() { + None => { + let field_ty = &field.ty; + quote_expr!(cx, try!(visitor.visit::<$field_ty>())) + } + Some(path) => { + let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with( + cx, builder, type_ident, impl_generics, &field.ty, path); + quote_expr!(cx, { + $wrapper + $wrapper_impl + try!(visitor.visit::<$wrapper_ty>()).map(|wrap| wrap.value) + }) + } + }; quote_stmt!(cx, - let $name = match try!(visitor.visit()) { - Some(value) => { $deserialize_with(value) }, + let $name = match $visit { + Some(value) => { value }, None => { return Err(_serde::de::Error::end_of_stream()); } @@ -587,14 +603,18 @@ fn deserialize_struct( let visit_seq_expr = try!(deserialize_struct_as_seq( cx, builder, + type_ident, type_path.clone(), + impl_generics, &fields_with_attrs, )); let (field_visitor, fields_stmt, visit_map_expr) = try!(deserialize_struct_visitor( cx, builder, + type_ident, type_path.clone(), + impl_generics, &fields_with_attrs, container_attrs, )); @@ -843,14 +863,18 @@ fn deserialize_struct_variant( let visit_seq_expr = try!(deserialize_struct_as_seq( cx, builder, + type_ident, type_path.clone(), + generics, &fields_with_attrs, )); let (field_visitor, fields_stmt, field_expr) = try!(deserialize_struct_visitor( cx, builder, + type_ident, type_path, + generics, &fields_with_attrs, container_attrs, )); @@ -1011,10 +1035,8 @@ fn deserialize_field_visitor( fn deserialize(deserializer: &mut D) -> ::std::result::Result<__Field, D::Error> where D: _serde::de::Deserializer, { - use std::marker::PhantomData; - struct __FieldVisitor { - phantom: PhantomData + phantom: ::std::marker::PhantomData } impl<__D> _serde::de::Visitor for __FieldVisitor<__D> @@ -1041,7 +1063,11 @@ fn deserialize_field_visitor( } } - deserializer.deserialize_struct_field(__FieldVisitor::{ phantom: PhantomData }) + deserializer.deserialize_struct_field( + __FieldVisitor::{ + phantom: ::std::marker::PhantomData + } + ) } } ).unwrap(); @@ -1052,7 +1078,9 @@ fn deserialize_field_visitor( fn deserialize_struct_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, + type_ident: Ident, struct_path: ast::Path, + impl_generics: &ast::Generics, fields: &[(&ast::StructField, attr::FieldAttrs)], container_attrs: &attr::ContainerAttrs, ) -> Result<(Vec>, ast::Stmt, P), Error> { @@ -1071,7 +1099,9 @@ fn deserialize_struct_visitor( let visit_map_expr = try!(deserialize_map( cx, builder, + type_ident, struct_path, + impl_generics, fields, container_attrs, )); @@ -1100,7 +1130,9 @@ fn deserialize_struct_visitor( fn deserialize_map( cx: &ExtCtxt, builder: &aster::AstBuilder, + type_ident: Ident, struct_path: ast::Path, + impl_generics: &ast::Generics, fields: &[(&ast::StructField, attr::FieldAttrs)], container_attrs: &attr::ContainerAttrs, ) -> Result, Error> { @@ -1114,17 +1146,34 @@ fn deserialize_map( // Declare each field that will be deserialized. let let_values: Vec = fields_attrs_names.iter() .filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field()) - .map(|&(_, _, name)| quote_stmt!(cx, let mut $name = None;).unwrap()) + .map(|&(field, _, name)| { + let field_ty = &field.ty; + quote_stmt!(cx, let mut $name: Option<$field_ty> = None;).unwrap() + }) .collect(); // Match arms to extract a value for a field. let value_arms = fields_attrs_names.iter() .filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field()) - .map(|&(_, ref attrs, name)| { - let deserialize_with = attrs.deserialize_with(); + .map(|&(field, ref attrs, name)| { + let visit = match attrs.deserialize_with() { + None => { + let field_ty = &field.ty; + quote_expr!(cx, try!(visitor.visit_value::<$field_ty>())) + } + Some(path) => { + let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with( + cx, builder, type_ident, impl_generics, &field.ty, path); + quote_expr!(cx, ({ + $wrapper + $wrapper_impl + try!(visitor.visit_value::<$wrapper_ty>()).value + })) + } + }; quote_arm!(cx, __Field::$name => { - $name = Some($deserialize_with(try!(visitor.visit_value()))); + $name = Some($visit); } ) }) @@ -1192,7 +1241,7 @@ fn deserialize_map( Ok(quote_expr!(cx, { $let_values - while let Some(key) = try!(visitor.visit_key()) { + while let Some(key) = try!(visitor.visit_key::<__Field>()) { match key { $value_arms $skipped_arms @@ -1222,3 +1271,55 @@ fn fields_with_attrs<'a>( }) .collect() } + +/// This function wraps the expression in `#[serde(deserialize_with="...")]` in +/// a trait to prevent it from accessing the internal `Deserialize` state. +fn wrap_deserialize_with( + cx: &ExtCtxt, + builder: &aster::AstBuilder, + type_ident: Ident, + impl_generics: &ast::Generics, + field_ty: &P, + deserialize_with: &ast::Path, +) -> (ast::Stmt, ast::Stmt, ast::Path) { + // Quasi-quoting doesn't do a great job of expanding generics into paths, + // so manually build it. + let wrapper_ty = builder.path() + .segment("__SerdeDeserializeWithStruct") + .with_generics(impl_generics.clone()) + .build() + .build(); + + let where_clause = &impl_generics.where_clause; + + let phantom_ty = builder.path() + .segment(type_ident) + .with_generics(builder.from_generics(impl_generics.clone()) + .strip_ty_params() + .build()) + .build() + .build(); + + ( + quote_stmt!(cx, + struct __SerdeDeserializeWithStruct $impl_generics $where_clause { + value: $field_ty, + phantom: ::std::marker::PhantomData<$phantom_ty>, + } + ).unwrap(), + quote_stmt!(cx, + impl $impl_generics _serde::de::Deserialize for $wrapper_ty $where_clause { + fn deserialize(__d: &mut D) -> ::std::result::Result + where D: _serde::de::Deserializer + { + let value = try!($deserialize_with(__d)); + Ok(__SerdeDeserializeWithStruct { + value: value, + phantom: ::std::marker::PhantomData, + }) + } + } + ).unwrap(), + wrapper_ty, + ) +} diff --git a/serde_tests/tests/test.rs.in b/serde_tests/tests/test.rs.in index 89a1b345..f64b84c1 100644 --- a/serde_tests/tests/test.rs.in +++ b/serde_tests/tests/test.rs.in @@ -6,5 +6,6 @@ mod token; mod test_annotations; mod test_bytes; mod test_de; +mod test_gen; mod test_macros; mod test_ser; diff --git a/serde_tests/tests/test_de.rs b/serde_tests/tests/test_de.rs index 1859cee9..601710ad 100644 --- a/serde_tests/tests/test_de.rs +++ b/serde_tests/tests/test_de.rs @@ -3,7 +3,7 @@ use std::net; use std::path::PathBuf; extern crate serde; -use self::serde::de::{Deserializer, Visitor}; +use self::serde::de::Deserializer; use token::{ Error, diff --git a/serde_tests/tests/test_gen.rs b/serde_tests/tests/test_gen.rs new file mode 100644 index 00000000..805c100d --- /dev/null +++ b/serde_tests/tests/test_gen.rs @@ -0,0 +1,56 @@ +// These just test that serde_codegen is able to produce code that compiles +// successfully when there are a variety of generics involved. + +extern crate serde; +use self::serde::ser::{Serialize, Serializer}; +use self::serde::de::{Deserialize, Deserializer}; + +////////////////////////////////////////////////////////////////////////// + +#[derive(Serialize, Deserialize)] +struct With { + t: T, + #[serde(serialize_with="ser_i32", deserialize_with="de_i32")] + i: i32, +} + +#[derive(Serialize, Deserialize)] +struct WithRef<'a, T: 'a> { + #[serde(skip_deserializing)] + t: Option<&'a T>, + #[serde(serialize_with="ser_i32", deserialize_with="de_i32")] + i: i32, +} + +#[derive(Serialize, Deserialize)] +struct Bounds { + t: T, + option: Option, + boxed: Box, + option_boxed: Option>, +} + +#[derive(Serialize, Deserialize)] +struct NoBounds { + t: T, + option: Option, + boxed: Box, + option_boxed: Option>, +} + +#[derive(Serialize, Deserialize)] +enum EnumWith { + A( + #[serde(serialize_with="ser_i32", deserialize_with="de_i32")] + i32), + B { + t: T, + #[serde(serialize_with="ser_i32", deserialize_with="de_i32")] + i: i32 }, +} + +////////////////////////////////////////////////////////////////////////// + +fn ser_i32(_: &i32, _: &mut S) -> Result<(), S::Error> { panic!() } + +fn de_i32(_: &mut D) -> Result { panic!() }