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!() }