Attribute for handwritten where clauses

This commit is contained in:
David Tolnay
2016-05-20 22:03:20 -07:00
parent 7052833512
commit 660ea7bd7b
5 changed files with 328 additions and 132 deletions
+121 -26
View File
@@ -4,7 +4,7 @@ use syntax::attr;
use syntax::codemap::Span; use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt; use syntax::ext::base::ExtCtxt;
use syntax::fold::Folder; use syntax::fold::Folder;
use syntax::parse::parser::PathStyle; use syntax::parse::parser::{Parser, PathStyle};
use syntax::parse::token::{self, InternedString}; use syntax::parse::token::{self, InternedString};
use syntax::parse; use syntax::parse;
use syntax::print::pprust::{lit_to_string, meta_item_to_string}; use syntax::print::pprust::{lit_to_string, meta_item_to_string};
@@ -62,6 +62,8 @@ impl Name {
pub struct ContainerAttrs { pub struct ContainerAttrs {
name: Name, name: Name,
deny_unknown_fields: bool, deny_unknown_fields: bool,
ser_where: Option<Vec<ast::WherePredicate>>,
de_where: Option<Vec<ast::WherePredicate>>,
} }
impl ContainerAttrs { impl ContainerAttrs {
@@ -70,6 +72,8 @@ impl ContainerAttrs {
let mut container_attrs = ContainerAttrs { let mut container_attrs = ContainerAttrs {
name: Name::new(item.ident), name: Name::new(item.ident),
deny_unknown_fields: false, deny_unknown_fields: false,
ser_where: None,
de_where: None,
}; };
for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) { for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) {
@@ -96,6 +100,20 @@ impl ContainerAttrs {
container_attrs.deny_unknown_fields = true; container_attrs.deny_unknown_fields = true;
} }
// Parse `#[serde(where="D: Serialize")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"where" => {
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
container_attrs.ser_where = Some(where_predicates.clone());
container_attrs.de_where = Some(where_predicates.clone());
}
// Parse `#[serde(where(serialize="D: Serialize", deserialize="D: Deserialize"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"where" => {
let (ser_where, de_where) = try!(get_where_predicates(cx, meta_items));
container_attrs.ser_where = ser_where;
container_attrs.de_where = de_where;
}
_ => { _ => {
cx.span_err( cx.span_err(
meta_item.span, meta_item.span,
@@ -118,6 +136,14 @@ impl ContainerAttrs {
pub fn deny_unknown_fields(&self) -> bool { pub fn deny_unknown_fields(&self) -> bool {
self.deny_unknown_fields self.deny_unknown_fields
} }
pub fn ser_where(&self) -> Option<&Vec<ast::WherePredicate>> {
self.ser_where.as_ref()
}
pub fn de_where(&self) -> Option<&Vec<ast::WherePredicate>> {
self.de_where.as_ref()
}
} }
/// Represents variant attribute information /// Represents variant attribute information
@@ -181,6 +207,8 @@ pub struct FieldAttrs {
default_expr_if_missing: Option<P<ast::Expr>>, default_expr_if_missing: Option<P<ast::Expr>>,
serialize_with: Option<ast::Path>, serialize_with: Option<ast::Path>,
deserialize_with: Option<ast::Path>, deserialize_with: Option<ast::Path>,
ser_where: Option<Vec<ast::WherePredicate>>,
de_where: Option<Vec<ast::WherePredicate>>,
} }
impl FieldAttrs { impl FieldAttrs {
@@ -203,6 +231,8 @@ impl FieldAttrs {
default_expr_if_missing: None, default_expr_if_missing: None,
serialize_with: None, serialize_with: None,
deserialize_with: None, deserialize_with: None,
ser_where: None,
de_where: None,
}; };
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) { for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
@@ -274,6 +304,20 @@ impl FieldAttrs {
field_attrs.deserialize_with = Some(path); field_attrs.deserialize_with = Some(path);
} }
// Parse `#[serde(where="D: Serialize")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"where" => {
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
field_attrs.ser_where = Some(where_predicates.clone());
field_attrs.de_where = Some(where_predicates.clone());
}
// Parse `#[serde(where(serialize="D: Serialize", deserialize="D: Deserialize"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"where" => {
let (ser_where, de_where) = try!(get_where_predicates(cx, meta_items));
field_attrs.ser_where = ser_where;
field_attrs.de_where = de_where;
}
_ => { _ => {
cx.span_err( cx.span_err(
meta_item.span, meta_item.span,
@@ -316,45 +360,59 @@ impl FieldAttrs {
pub fn deserialize_with(&self) -> Option<&ast::Path> { pub fn deserialize_with(&self) -> Option<&ast::Path> {
self.deserialize_with.as_ref() self.deserialize_with.as_ref()
} }
pub fn ser_where(&self) -> Option<&Vec<ast::WherePredicate>> {
self.ser_where.as_ref()
}
pub fn de_where(&self) -> Option<&Vec<ast::WherePredicate>> {
self.de_where.as_ref()
}
} }
/// Zip together fields and `#[serde(...)]` attributes on those fields. /// Zip together fields and `#[serde(...)]` attributes on those fields.
pub fn fields_with_attrs<'a>( pub fn fields_with_attrs(
cx: &ExtCtxt, cx: &ExtCtxt,
fields: &'a [ast::StructField], fields: &[ast::StructField],
) -> Result<Vec<(&'a ast::StructField, FieldAttrs)>, Error> { ) -> Result<Vec<(ast::StructField, FieldAttrs)>, Error> {
fields.iter() fields.iter()
.enumerate() .enumerate()
.map(|(i, field)| { .map(|(i, field)| {
let attrs = try!(FieldAttrs::from_field(cx, i, field)); let attrs = try!(FieldAttrs::from_field(cx, i, field));
Ok((field, attrs)) Ok((field.clone(), attrs))
}) })
.collect() .collect()
} }
fn get_renames(cx: &ExtCtxt, fn get_ser_and_de<T, F>(
items: &[P<ast::MetaItem>], cx: &ExtCtxt,
)-> Result<(Option<InternedString>, Option<InternedString>), Error> { attribute: &str,
let mut ser_name = None; items: &[P<ast::MetaItem>],
let mut de_name = None; f: F
) -> Result<(Option<T>, Option<T>), Error>
where F: Fn(&ExtCtxt, &str, &ast::Lit) -> Result<T, Error>,
{
let mut ser_item = None;
let mut de_item = None;
for item in items { for item in items {
match item.node { match item.node {
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => {
let s = try!(get_str_from_lit(cx, name, lit)); let s = try!(f(cx, name, lit));
ser_name = Some(s); ser_item = Some(s);
} }
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => {
let s = try!(get_str_from_lit(cx, name, lit)); let s = try!(f(cx, name, lit));
de_name = Some(s); de_item = Some(s);
} }
_ => { _ => {
cx.span_err( cx.span_err(
item.span, item.span,
&format!("unknown rename attribute `{}`", &format!("unknown {} attribute `{}`",
attribute,
meta_item_to_string(item))); meta_item_to_string(item)));
return Err(Error); return Err(Error);
@@ -362,7 +420,21 @@ fn get_renames(cx: &ExtCtxt,
} }
} }
Ok((ser_name, de_name)) Ok((ser_item, de_item))
}
fn get_renames(
cx: &ExtCtxt,
items: &[P<ast::MetaItem>],
) -> Result<(Option<InternedString>, Option<InternedString>), Error> {
get_ser_and_de(cx, "rename", items, get_str_from_lit)
}
fn get_where_predicates(
cx: &ExtCtxt,
items: &[P<ast::MetaItem>],
) -> Result<(Option<Vec<ast::WherePredicate>>, Option<Vec<ast::WherePredicate>>), Error> {
get_ser_and_de(cx, "where", items, parse_lit_into_where)
} }
pub fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> { pub fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
@@ -437,17 +509,18 @@ fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<Interned
} }
} }
fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::Path, Error> { // If we just parse a string literal from an attibute, any syntax errors in the
let source = try!(get_str_from_lit(cx, name, lit)); // source will only have spans that point inside the string and not back to the
// attribute. So to have better error reporting, we'll first parse the string
// If we just parse the string into an expression, any syntax errors in the source will only // into a token tree. Then we'll update those spans to say they're coming from a
// have spans that point inside the string, and not back to the attribute. So to have better // macro context that originally came from the attribnute, and then finally
// error reporting, we'll first parse the string into a token tree. Then we'll update those // parse them into an expression or where-clause.
// spans to say they're coming from a macro context that originally came from the attribute, fn parse_string_via_tts<T, F>(cx: &ExtCtxt, name: &str, string: String, action: F) -> Result<T, Error>
// and then finally parse them into an expression. where F: for<'a> Fn(&'a mut Parser) -> parse::PResult<'a, T>,
{
let tts = panictry!(parse::parse_tts_from_source_str( let tts = panictry!(parse::parse_tts_from_source_str(
format!("<serde {} expansion>", name), format!("<serde {} expansion>", name),
(*source).to_owned(), string,
cx.cfg(), cx.cfg(),
cx.parse_sess())); cx.parse_sess()));
@@ -456,7 +529,7 @@ fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::
let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts); let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts);
let path = match parser.parse_path(PathStyle::Type) { let path = match action(&mut parser) {
Ok(path) => path, Ok(path) => path,
Err(mut e) => { Err(mut e) => {
e.emit(); e.emit();
@@ -476,6 +549,28 @@ fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::
Ok(path) Ok(path)
} }
fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::Path, Error> {
let string = try!(get_str_from_lit(cx, name, lit)).to_string();
parse_string_via_tts(cx, name, string, |parser| {
parser.parse_path(PathStyle::Type)
})
}
fn parse_lit_into_where(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<Vec<ast::WherePredicate>, Error> {
let string = try!(get_str_from_lit(cx, name, lit));
if string.is_empty() {
return Ok(Vec::new());
}
let where_string = format!("where {}", string);
parse_string_via_tts(cx, name, where_string, |parser| {
let where_clause = try!(parser.parse_where_clause());
Ok(where_clause.predicates)
})
}
/// This function wraps the expression in `#[serde(default="...")]` in a function to prevent it /// This function wraps the expression in `#[serde(default="...")]` in a function to prevent it
/// from accessing the internal `Deserialize` state. /// from accessing the internal `Deserialize` state.
fn wrap_default(path: ast::Path) -> P<ast::Expr> { fn wrap_default(path: ast::Path) -> P<ast::Expr> {
+56 -10
View File
@@ -7,6 +7,9 @@ use syntax::ext::base::ExtCtxt;
use syntax::ptr::P; use syntax::ptr::P;
use syntax::visit; use syntax::visit;
use attr;
use error::Error;
// Remove the default from every type parameter because in the generated impls // Remove the default from every type parameter because in the generated impls
// they look like associated types: "error: associated type bindings are not // they look like associated types: "error: associated type bindings are not
// allowed here". // allowed here".
@@ -21,20 +24,50 @@ pub fn without_defaults(generics: &ast::Generics) -> ast::Generics {
} }
} }
pub fn with_bound( pub fn with_where_predicates(
builder: &AstBuilder,
generics: &ast::Generics,
predicates: &Vec<ast::WherePredicate>,
) -> ast::Generics {
builder.from_generics(generics.clone())
.with_predicates(predicates.clone())
.build()
}
pub fn with_where_predicates_from_fields<F>(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &AstBuilder, builder: &AstBuilder,
item: &ast::Item, item: &ast::Item,
generics: &ast::Generics, generics: &ast::Generics,
filter: &Fn(&ast::StructField) -> bool, from_field: F,
bound: &ast::Path, ) -> Result<ast::Generics, Error>
) -> ast::Generics { where F: Fn(&attr::FieldAttrs) -> Option<&Vec<ast::WherePredicate>>,
builder.from_generics(generics.clone()) {
Ok(builder.from_generics(generics.clone())
.with_predicates( .with_predicates(
all_variants(cx, item).iter() try!(all_fields_with_attrs(cx, item))
.flat_map(|variant_data| all_struct_fields(variant_data)) .iter()
.filter(|field| filter(field)) .flat_map(|&(_, ref attrs)| from_field(attrs))
.map(|field| &field.ty) .flat_map(|predicates| predicates.clone()))
.build())
}
pub fn with_bound<F>(
cx: &ExtCtxt,
builder: &AstBuilder,
item: &ast::Item,
generics: &ast::Generics,
filter: F,
bound: &ast::Path,
) -> Result<ast::Generics, Error>
where F: Fn(&ast::StructField, &attr::FieldAttrs) -> bool,
{
Ok(builder.from_generics(generics.clone())
.with_predicates(
try!(all_fields_with_attrs(cx, item))
.iter()
.filter(|&&(ref field, ref attrs)| filter(field, attrs))
.map(|&(ref field, _)| &field.ty)
// TODO this filter can be removed later, see comment on function // TODO this filter can be removed later, see comment on function
.filter(|ty| contains_generic(ty, generics)) .filter(|ty| contains_generic(ty, generics))
.map(|ty| strip_reference(ty)) .map(|ty| strip_reference(ty))
@@ -44,7 +77,20 @@ pub fn with_bound(
// the bound e.g. Serialize // the bound e.g. Serialize
.bound().trait_(bound.clone()).build() .bound().trait_(bound.clone()).build()
.build())) .build()))
.build() .build())
}
fn all_fields_with_attrs(
cx: &ExtCtxt,
item: &ast::Item,
) -> Result<Vec<(ast::StructField, attr::FieldAttrs)>, Error> {
let fields: Vec<ast::StructField> =
all_variants(cx, item).iter()
.flat_map(|variant_data| all_struct_fields(variant_data))
.cloned()
.collect();
attr::fields_with_attrs(cx, &fields)
} }
fn all_variants<'a>(cx: &ExtCtxt, item: &'a ast::Item) -> Vec<&'a ast::VariantData> { fn all_variants<'a>(cx: &ExtCtxt, item: &'a ast::Item) -> Vec<&'a ast::VariantData> {
+78 -65
View File
@@ -35,36 +35,53 @@ pub fn expand_derive_deserialize(
let builder = aster::AstBuilder::new().span(span); let builder = aster::AstBuilder::new().span(span);
let generics = match item.node { let impl_item = match deserialize_item(cx, &builder, &item) {
ast::ItemKind::Struct(_, ref generics) => generics, Ok(item) => item,
ast::ItemKind::Enum(_, ref generics) => generics,
_ => {
cx.span_err(
meta_item.span,
"`#[derive(Deserialize)]` may only be applied to structs and enums");
return;
}
};
let impl_generics = build_impl_generics(cx, &builder, item, generics);
let ty = builder.ty().path()
.segment(item.ident).with_generics(impl_generics.clone()).build()
.build();
let body = match deserialize_body(cx, &builder, &item, &impl_generics, ty.clone()) {
Ok(body) => body,
Err(Error) => { Err(Error) => {
// An error occured, but it should have been reported already. // An error occured, but it should have been reported already.
return; return;
} }
}; };
push(Annotatable::Item(impl_item))
}
fn deserialize_item(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
item: &Item,
) -> Result<P<ast::Item>, Error> {
let generics = match item.node {
ast::ItemKind::Struct(_, ref generics) => generics,
ast::ItemKind::Enum(_, ref generics) => generics,
_ => {
cx.span_err(
item.span,
"`#[derive(Deserialize)]` may only be applied to structs and enums");
return Err(Error);
}
};
let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
let impl_generics = try!(build_impl_generics(cx, &builder, item, generics, &container_attrs));
let ty = builder.ty().path()
.segment(item.ident).with_generics(impl_generics.clone()).build()
.build();
let body = try!(deserialize_body(cx,
&builder,
&item,
&impl_generics,
ty.clone(),
&container_attrs));
let where_clause = &impl_generics.where_clause; let where_clause = &impl_generics.where_clause;
let dummy_const = builder.id(format!("_IMPL_DESERIALIZE_FOR_{}", item.ident)); let dummy_const = builder.id(format!("_IMPL_DESERIALIZE_FOR_{}", item.ident));
let impl_item = quote_item!(cx, Ok(quote_item!(cx,
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const $dummy_const: () = { const $dummy_const: () = {
extern crate serde as _serde; extern crate serde as _serde;
@@ -77,9 +94,7 @@ pub fn expand_derive_deserialize(
} }
} }
}; };
).unwrap(); ).unwrap())
push(Annotatable::Item(impl_item))
} }
// All the generics in the input, plus a bound `T: Deserialize` for each generic // All the generics in the input, plus a bound `T: Deserialize` for each generic
@@ -90,41 +105,43 @@ fn build_impl_generics(
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
item: &Item, item: &Item,
generics: &ast::Generics, generics: &ast::Generics,
) -> ast::Generics { container_attrs: &attr::ContainerAttrs,
) -> Result<ast::Generics, Error> {
let generics = bound::without_defaults(generics); let generics = bound::without_defaults(generics);
let generics = bound::with_bound(cx, builder, item, &generics,
&deserialized_by_us, let generics = try!(bound::with_where_predicates_from_fields(
&builder.path().ids(&["_serde", "de", "Deserialize"]).build()); cx, builder, item, &generics,
let generics = bound::with_bound(cx, builder, item, &generics, |attrs| attrs.de_where()));
&requires_default,
&builder.path().global().ids(&["std", "default", "Default"]).build()); match container_attrs.de_where() {
generics Some(predicates) => {
let generics = bound::with_where_predicates(builder, &generics, predicates);
Ok(generics)
}
None => {
let generics = try!(bound::with_bound(cx, builder, item, &generics,
deserialized_by_us,
&builder.path().ids(&["_serde", "de", "Deserialize"]).build()));
let generics = try!(bound::with_bound(cx, builder, item, &generics,
requires_default,
&builder.path().global().ids(&["std", "default", "Default"]).build()));
Ok(generics)
}
}
} }
// Fields with a `skip_deserializing` or `deserialize_with` attribute are not // Fields with a `skip_deserializing` or `deserialize_with` attribute are not
// deserialized by us. All other fields may need a `T: Deserialize` bound where // deserialized by us. All other fields may need a `T: Deserialize` bound where
// T is the type of the field. // T is the type of the field.
fn deserialized_by_us(field: &ast::StructField) -> bool { fn deserialized_by_us(_: &ast::StructField, attrs: &attr::FieldAttrs) -> bool {
for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) { !attrs.skip_deserializing_field()
for meta_item in meta_items { && attrs.deserialize_with().is_none()
match meta_item.node { && attrs.de_where().is_none()
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
return false
}
ast::MetaItemKind::NameValue(ref name, _) if name == &"deserialize_with" => {
return false
}
_ => {}
}
}
}
true
} }
// Fields with a `default` attribute (not `default=...`), and fields with a // Fields with a `default` attribute (not `default=...`), and fields with a
// `skip_deserializing` attribute that do not also have `default=...`. // `skip_deserializing` attribute that do not also have `default=...`.
fn requires_default(field: &ast::StructField) -> bool { fn requires_default(field: &ast::StructField, attrs: &attr::FieldAttrs) -> bool {
let mut has_skip_deserializing = false;
for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) { for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) {
for meta_item in meta_items { for meta_item in meta_items {
match meta_item.node { match meta_item.node {
@@ -134,14 +151,11 @@ fn requires_default(field: &ast::StructField) -> bool {
ast::MetaItemKind::NameValue(ref name, _) if name == &"default" => { ast::MetaItemKind::NameValue(ref name, _) if name == &"default" => {
return false return false
} }
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
has_skip_deserializing = true
}
_ => {} _ => {}
} }
} }
} }
has_skip_deserializing attrs.skip_deserializing_field()
} }
fn deserialize_body( fn deserialize_body(
@@ -150,9 +164,8 @@ fn deserialize_body(
item: &Item, item: &Item,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
match item.node { match item.node {
ast::ItemKind::Struct(ref variant_data, _) => { ast::ItemKind::Struct(ref variant_data, _) => {
deserialize_item_struct( deserialize_item_struct(
@@ -163,7 +176,7 @@ fn deserialize_body(
ty, ty,
item.span, item.span,
variant_data, variant_data,
&container_attrs, container_attrs,
) )
} }
ast::ItemKind::Enum(ref enum_def, _) => { ast::ItemKind::Enum(ref enum_def, _) => {
@@ -174,7 +187,7 @@ fn deserialize_body(
impl_generics, impl_generics,
ty, ty,
enum_def, enum_def,
&container_attrs, container_attrs,
) )
} }
_ => { _ => {
@@ -441,12 +454,12 @@ fn deserialize_seq(
type_ident: Ident, type_ident: Ident,
type_path: ast::Path, type_path: ast::Path,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
fields: &[(&ast::StructField, attr::FieldAttrs)], fields: &[(ast::StructField, attr::FieldAttrs)],
is_struct: bool, is_struct: bool,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let let_values: Vec<_> = fields.iter() let let_values: Vec<_> = fields.iter()
.enumerate() .enumerate()
.map(|(i, &(field, ref attrs))| { .map(|(i, &(ref field, ref attrs))| {
let name = builder.id(format!("__field{}", i)); let name = builder.id(format!("__field{}", i));
if attrs.skip_deserializing_field() { if attrs.skip_deserializing_field() {
let default = expr_is_missing(cx, attrs); let default = expr_is_missing(cx, attrs);
@@ -486,7 +499,7 @@ fn deserialize_seq(
.with_id_exprs( .with_id_exprs(
fields.iter() fields.iter()
.enumerate() .enumerate()
.map(|(i, &(field, _))| { .map(|(i, &(ref field, _))| {
( (
match field.ident { match field.ident {
Some(name) => name.clone(), Some(name) => name.clone(),
@@ -521,9 +534,9 @@ fn deserialize_newtype_struct(
type_ident: Ident, type_ident: Ident,
type_path: &ast::Path, type_path: &ast::Path,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
field: &(&ast::StructField, attr::FieldAttrs), field: &(ast::StructField, attr::FieldAttrs),
) -> Result<Vec<ast::TokenTree>, Error> { ) -> Result<Vec<ast::TokenTree>, Error> {
let &(field, ref attrs) = field; let &(ref field, ref attrs) = field;
let value = match attrs.deserialize_with() { let value = match attrs.deserialize_with() {
None => { None => {
let field_ty = &field.ty; let field_ty = &field.ty;
@@ -982,7 +995,7 @@ fn deserialize_struct_visitor(
type_ident: Ident, type_ident: Ident,
struct_path: ast::Path, struct_path: ast::Path,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
fields: &[(&ast::StructField, attr::FieldAttrs)], fields: &[(ast::StructField, attr::FieldAttrs)],
container_attrs: &attr::ContainerAttrs, container_attrs: &attr::ContainerAttrs,
) -> Result<(Vec<P<ast::Item>>, ast::Stmt, P<ast::Expr>), Error> { ) -> Result<(Vec<P<ast::Item>>, ast::Stmt, P<ast::Expr>), Error> {
let field_exprs = fields.iter() let field_exprs = fields.iter()
@@ -1010,7 +1023,7 @@ fn deserialize_struct_visitor(
let fields_expr = builder.expr().ref_().slice() let fields_expr = builder.expr().ref_().slice()
.with_exprs( .with_exprs(
fields.iter() fields.iter()
.map(|&(field, _)| { .map(|&(ref field, _)| {
match field.ident { match field.ident {
Some(name) => builder.expr().str(name), Some(name) => builder.expr().str(name),
None => { None => {
@@ -1034,7 +1047,7 @@ fn deserialize_map(
type_ident: Ident, type_ident: Ident,
struct_path: ast::Path, struct_path: ast::Path,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
fields: &[(&ast::StructField, attr::FieldAttrs)], fields: &[(ast::StructField, attr::FieldAttrs)],
container_attrs: &attr::ContainerAttrs, container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
// Create the field names for the fields. // Create the field names for the fields.
@@ -1056,7 +1069,7 @@ fn deserialize_map(
// Match arms to extract a value for a field. // Match arms to extract a value for a field.
let value_arms = fields_attrs_names.iter() let value_arms = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field()) .filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
.map(|&(field, ref attrs, name)| { .map(|&(ref field, ref attrs, name)| {
let deser_name = attrs.name().deserialize_name(); let deser_name = attrs.name().deserialize_name();
let name_str = builder.expr().lit().str(deser_name); let name_str = builder.expr().lit().str(deser_name);
+37 -31
View File
@@ -60,7 +60,9 @@ fn serialize_item(
} }
}; };
let impl_generics = build_impl_generics(cx, builder, item, generics); let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
let impl_generics = try!(build_impl_generics(cx, builder, item, generics, &container_attrs));
let ty = builder.ty().path() let ty = builder.ty().path()
.segment(item.ident).with_generics(impl_generics.clone()).build() .segment(item.ident).with_generics(impl_generics.clone()).build()
@@ -70,7 +72,8 @@ fn serialize_item(
&builder, &builder,
&item, &item,
&impl_generics, &impl_generics,
ty.clone())); ty.clone(),
&container_attrs));
let where_clause = &impl_generics.where_clause; let where_clause = &impl_generics.where_clause;
@@ -99,32 +102,36 @@ fn build_impl_generics(
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
item: &Item, item: &Item,
generics: &ast::Generics, generics: &ast::Generics,
) -> ast::Generics { container_attrs: &attr::ContainerAttrs,
) -> Result<ast::Generics, Error> {
let generics = bound::without_defaults(generics); let generics = bound::without_defaults(generics);
let generics = bound::with_bound(cx, builder, item, &generics,
&serialized_by_us, let generics = try!(bound::with_where_predicates_from_fields(
&builder.path().ids(&["_serde", "ser", "Serialize"]).build()); cx, builder, item, &generics,
generics |attrs| attrs.ser_where()));
match container_attrs.ser_where() {
Some(predicates) => {
let generics = bound::with_where_predicates(builder, &generics, predicates);
Ok(generics)
}
None => {
let generics = try!(bound::with_bound(cx, builder, item, &generics,
needs_serialize_bound,
&builder.path().ids(&["_serde", "ser", "Serialize"]).build()));
Ok(generics)
}
}
} }
// Fields with a `skip_serializing` or `serialize_with` attribute are not // Fields with a `skip_serializing` or `serialize_with` attribute are not
// serialized by us. All other fields may need a `T: Serialize` bound where T is // serialized by us so we do not generate a bound. Fields with a `where`
// the type of the field. // attribute specify their own bound so we do not generate one. All other fields
fn serialized_by_us(field: &ast::StructField) -> bool { // may need a `T: Serialize` bound where T is the type of the field.
for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) { fn needs_serialize_bound(_: &ast::StructField, attrs: &attr::FieldAttrs) -> bool {
for meta_item in meta_items { !attrs.skip_serializing_field()
match meta_item.node { && attrs.serialize_with().is_none()
ast::MetaItemKind::Word(ref name) if name == &"skip_serializing" => { && attrs.ser_where().is_none()
return false
}
ast::MetaItemKind::NameValue(ref name, _) if name == &"serialize_with" => {
return false
}
_ => {}
}
}
}
true
} }
fn serialize_body( fn serialize_body(
@@ -133,9 +140,8 @@ fn serialize_body(
item: &Item, item: &Item,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
match item.node { match item.node {
ast::ItemKind::Struct(ref variant_data, _) => { ast::ItemKind::Struct(ref variant_data, _) => {
serialize_item_struct( serialize_item_struct(
@@ -145,7 +151,7 @@ fn serialize_body(
ty, ty,
item.span, item.span,
variant_data, variant_data,
&container_attrs, container_attrs,
) )
} }
ast::ItemKind::Enum(ref enum_def, _) => { ast::ItemKind::Enum(ref enum_def, _) => {
@@ -156,7 +162,7 @@ fn serialize_body(
impl_generics, impl_generics,
ty, ty,
enum_def, enum_def,
&container_attrs, container_attrs,
) )
} }
_ => { _ => {
@@ -661,7 +667,7 @@ fn serialize_tuple_struct_visitor(
let arms: Vec<_> = fields_with_attrs.iter() let arms: Vec<_> = fields_with_attrs.iter()
.enumerate() .enumerate()
.map(|(i, &(field, ref attrs))| { .map(|(i, &(ref field, ref attrs))| {
let mut field_expr = builder.expr().tup_field(i).field("value").self_(); let mut field_expr = builder.expr().tup_field(i).field("value").self_();
if !is_enum { if !is_enum {
field_expr = quote_expr!(cx, &$field_expr); field_expr = quote_expr!(cx, &$field_expr);
@@ -745,7 +751,7 @@ fn serialize_struct_visitor(
let arms: Vec<ast::Arm> = fields_with_attrs.iter() let arms: Vec<ast::Arm> = fields_with_attrs.iter()
.filter(|&&(_, ref attrs)| !attrs.skip_serializing_field()) .filter(|&&(_, ref attrs)| !attrs.skip_serializing_field())
.enumerate() .enumerate()
.map(|(i, &(field, ref attrs))| { .map(|(i, &(ref field, ref attrs))| {
let ident = field.ident.expect("struct has unnamed field"); let ident = field.ident.expect("struct has unnamed field");
let mut field_expr = quote_expr!(cx, self.value.$ident); let mut field_expr = quote_expr!(cx, self.value.$ident);
if !is_enum { if !is_enum {
@@ -789,7 +795,7 @@ fn serialize_struct_visitor(
let len = fields_with_attrs.iter() let len = fields_with_attrs.iter()
.filter(|&&(_, ref attrs)| !attrs.skip_serializing_field()) .filter(|&&(_, ref attrs)| !attrs.skip_serializing_field())
.map(|&(field, ref attrs)| { .map(|&(ref field, ref attrs)| {
let ident = field.ident.expect("struct has unnamed fields"); let ident = field.ident.expect("struct has unnamed fields");
let mut field_expr = quote_expr!(cx, self.value.$ident); let mut field_expr = quote_expr!(cx, self.value.$ident);
if !is_enum { if !is_enum {
+36
View File
@@ -74,8 +74,44 @@ struct Tuple<T>(
X, X,
); );
#[derive(Serialize, Deserialize)]
#[serde(where(serialize="D: Serialize", deserialize="D: Deserialize"))]
enum TreeNode<D> {
Split {
left: Box<TreeNode<D>>,
right: Box<TreeNode<D>>,
},
Leaf {
data: D,
},
}
#[derive(Serialize, Deserialize)]
struct ListNode<D> {
data: D,
#[serde(where="")]
next: Box<ListNode<D>>,
}
#[derive(Serialize, Deserialize)]
struct SerializeWithTrait<D> {
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with",
where(serialize="D: SerializeWith",
deserialize="D: DeserializeWith"))]
data: D,
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
trait SerializeWith {
fn serialize_with<S: Serializer>(_: &Self, _: &mut S) -> Result<(), S::Error>;
}
trait DeserializeWith: Sized {
fn deserialize_with<D: Deserializer>(_: &mut D) -> Result<Self, D::Error>;
}
// Implements neither Serialize nor Deserialize // Implements neither Serialize nor Deserialize
struct X; struct X;
fn ser_x<S: Serializer>(_: &X, _: &mut S) -> Result<(), S::Error> { panic!() } fn ser_x<S: Serializer>(_: &X, _: &mut S) -> Result<(), S::Error> { panic!() }