diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index b630ede0c..dae4ca65a 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -1,112 +1,69 @@ -use aster; - -use syntax::ast::{self, Ident, MetaItem}; -use syntax::codemap::Span; -use syntax::ext::base::{Annotatable, ExtCtxt}; -use syntax::parse::token::InternedString; -use syntax::ptr::P; -use syntax::tokenstream::TokenTree; +use syn::{self, aster}; +use quote::{self, Tokens}; use bound; -use span; use internals::ast::{Body, Field, Item, Style, Variant}; -use internals::{attr, Error}; - -pub fn expand_derive_deserialize( - cx: &mut ExtCtxt, - span: Span, - meta_item: &MetaItem, - annotatable: &Annotatable, - push: &mut FnMut(Annotatable) -) { - let item = match *annotatable { - Annotatable::Item(ref item) => item, - _ => { - cx.span_err( - meta_item.span, - "`#[derive(Deserialize)]` may only be applied to structs and enums"); - return; - } +use internals::{self, attr}; + +pub fn expand_derive_deserialize(item: &syn::Item) -> Tokens { + let item = { + let ctxt = internals::Ctxt::new(); + let item = Item::from_ast(&ctxt, item); + check_no_str(&ctxt, &item); + item }; - let item = match Item::from_ast(cx, item) { - Ok(item) => item, - Err(Error::UnexpectedItemKind) => { - cx.span_err(item.span, - "`#[derive(Deserialize)]` may only be applied to structs and enums"); - return; - } - }; - - if check_no_str(cx, &item).is_err() { - return; - } - - let builder = aster::AstBuilder::new().span(span); + let impl_generics = build_impl_generics(&item); - let impl_item = deserialize_item(cx, &builder, &item); - push(span::record_expansion(cx, impl_item, "Deserialize")) -} - -fn deserialize_item( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - item: &Item, -) -> P { - let impl_generics = build_impl_generics(builder, &item); - - let ty = builder.ty().path() - .segment(item.ident).with_generics(impl_generics.clone()).build() + let ty = aster::ty().path() + .segment(item.ident.clone()).with_generics(impl_generics.clone()).build() .build(); - let body = deserialize_body(cx, - builder, - &item, + let body = deserialize_body(&item, &impl_generics, ty.clone()); let where_clause = &impl_generics.where_clause; - let dummy_const = builder.id(format!("_IMPL_DESERIALIZE_FOR_{}", item.ident)); + let dummy_const = aster::id(format!("_IMPL_DESERIALIZE_FOR_{}", item.ident)); - quote_item!(cx, + quote! { #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] - const $dummy_const: () = { + const #dummy_const: () = { extern crate serde as _serde; #[automatically_derived] - impl $impl_generics _serde::de::Deserialize for $ty $where_clause { - fn deserialize<__D>(deserializer: &mut __D) -> ::std::result::Result<$ty, __D::Error> + impl #impl_generics _serde::de::Deserialize for #ty #where_clause { + fn deserialize<__D>(deserializer: &mut __D) -> ::std::result::Result<#ty, __D::Error> where __D: _serde::de::Deserializer - $body + { + #body + } } }; - ).unwrap() + } } // All the generics in the input, plus a bound `T: Deserialize` for each generic // field type that will be deserialized by us, plus a bound `T: Default` for // each generic field type that will be set to a default value. -fn build_impl_generics( - builder: &aster::AstBuilder, - item: &Item, -) -> ast::Generics { +fn build_impl_generics(item: &Item) -> syn::Generics { let generics = bound::without_defaults(item.generics); let generics = bound::with_where_predicates_from_fields( - builder, item, &generics, + item, &generics, |attrs| attrs.de_bound()); match item.attrs.de_bound() { Some(predicates) => { - bound::with_where_predicates(builder, &generics, predicates) + bound::with_where_predicates(&generics, predicates) } None => { - let generics = bound::with_bound(builder, item, &generics, + let generics = bound::with_bound(item, &generics, needs_deserialize_bound, - &builder.path().ids(&["_serde", "de", "Deserialize"]).build()); - let generics = bound::with_bound(builder, item, &generics, + &aster::path().ids(&["_serde", "de", "Deserialize"]).build()); + let generics = bound::with_bound(item, &generics, requires_default, - &builder.path().global().ids(&["std", "default", "Default"]).build()); + &aster::path().global().ids(&["std", "default", "Default"]).build()); generics } } @@ -129,18 +86,14 @@ fn requires_default(attrs: &attr::Field) -> bool { } fn deserialize_body( - cx: &ExtCtxt, - builder: &aster::AstBuilder, item: &Item, - impl_generics: &ast::Generics, - ty: P, -) -> P { + impl_generics: &syn::Generics, + ty: syn::Ty, +) -> Tokens { match item.body { Body::Enum(ref variants) => { deserialize_item_enum( - cx, - builder, - item.ident, + &item.ident, impl_generics, ty, variants, @@ -148,13 +101,11 @@ fn deserialize_body( } Body::Struct(Style::Struct, ref fields) => { if fields.iter().any(|field| field.ident.is_none()) { - cx.span_bug(item.span, "struct has unnamed fields") + panic!("struct has unnamed fields"); } deserialize_struct( - cx, - builder, - item.ident, + &item.ident, None, impl_generics, ty, @@ -164,13 +115,11 @@ fn deserialize_body( Body::Struct(Style::Tuple, ref fields) | Body::Struct(Style::Newtype, ref fields) => { if fields.iter().any(|field| field.ident.is_some()) { - cx.span_bug(item.span, "tuple struct has named fields") + panic!("tuple struct has named fields"); } deserialize_tuple( - cx, - builder, - item.ident, + &item.ident, None, impl_generics, ty, @@ -179,93 +128,97 @@ fn deserialize_body( } Body::Struct(Style::Unit, _) => { deserialize_unit_struct( - cx, - builder, - item.ident, + &item.ident, &item.attrs) } } } // Build `__Visitor(PhantomData, PhantomData, ...)` -fn deserialize_visitor( - builder: &aster::AstBuilder, - generics: &ast::Generics, -) -> (P, P, P) { +// +// Returns: +// +// 1. the struct declaration +// 2. the visitor type, including generics +// 3. the expression for instantiating the visitor +fn deserialize_visitor(generics: &syn::Generics) -> (Tokens, Tokens, Tokens) { if generics.lifetimes.is_empty() && generics.ty_params.is_empty() { ( - builder.item().unit_struct("__Visitor"), - builder.ty().id("__Visitor"), - builder.expr().id("__Visitor"), + quote! { + struct __Visitor; + }, + quote!(__Visitor), + quote!(__Visitor), ) } else { - let placeholders : Vec<_> = generics.ty_params.iter() - .map(|t| builder.ty().id(t.ident)) + let num_phantoms = generics.lifetimes.len() + generics.ty_params.len(); + + let phantom_types = generics.lifetimes.iter() + .map(|lifetime_def| { + let lifetime = &lifetime_def.lifetime; + quote!(::std::marker::PhantomData<& #lifetime ()>) + }).chain(generics.ty_params.iter() + .map(|ty_param| { + let ident = &ty_param.ident; + quote!(::std::marker::PhantomData<#ident>) + })); + + let all_params = generics.lifetimes.iter() + .map(|lifetime_def| { + let lifetime = &lifetime_def.lifetime; + quote!(#lifetime) + }).chain(generics.ty_params.iter() + .map(|ty_param| { + let ident = &ty_param.ident; + quote!(#ident) + })); + + let ty_param_idents: Vec<_> = generics.ty_params.iter() + .map(|t| { + let ident = &t.ident; + quote!(#ident) + }) .collect(); + let ty_param_idents = if ty_param_idents.is_empty() { + None + } else { + Some(quote!(::<#(ty_param_idents),*>)) + }; + + let phantom_exprs = (0 .. num_phantoms).map(|_| quote!(::std::marker::PhantomData)); + ( - builder.item().tuple_struct("__Visitor") - .generics().with(generics.clone()).build() - .with_tys({ - let lifetimes = generics.lifetimes.iter() - .map(|lifetime_def| { - builder.ty() - .phantom_data() - .ref_().lifetime(lifetime_def.lifetime.name) - .ty() - .unit() - }); - - let ty_params = generics.ty_params.iter() - .map(|ty_param| { - builder.ty() - .phantom_data() - .id(ty_param.ident) - }); - - lifetimes.chain(ty_params) - }) - .build(), - builder.ty().path() - .segment("__Visitor").with_generics(generics.clone()).build() - .build(), - builder.expr().call() - .path().segment("__Visitor") - .with_tys(placeholders) - .build().build() - .with_args({ - let len = generics.lifetimes.len() + generics.ty_params.len(); - - (0 .. len).map(|_| builder.expr().phantom_data()) - }) - .build(), + quote! { + struct __Visitor #generics ( #(phantom_types),* ); + }, + quote!(__Visitor <#(all_params),*> ), + quote!(__Visitor #ty_param_idents ( #(phantom_exprs),* )), ) } } fn deserialize_unit_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, + type_ident: &syn::Ident, item_attrs: &attr::Item, -) -> P { - let type_name = name_expr(builder, item_attrs.name()); +) -> Tokens { + let type_name = item_attrs.name().deserialize_name(); - quote_block!(cx, { + quote! { struct __Visitor; impl _serde::de::Visitor for __Visitor { - type Value = $type_ident; + type Value = #type_ident; #[inline] - fn visit_unit<__E>(&mut self) -> ::std::result::Result<$type_ident, __E> + fn visit_unit<__E>(&mut self) -> ::std::result::Result<#type_ident, __E> where __E: _serde::de::Error, { - Ok($type_ident) + Ok(#type_ident) } #[inline] - fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$type_ident, __V::Error> + fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<#type_ident, __V::Error> where __V: _serde::de::SeqVisitor, { try!(visitor.end()); @@ -273,39 +226,32 @@ fn deserialize_unit_struct( } } - deserializer.deserialize_unit_struct($type_name, __Visitor) - }).unwrap() + deserializer.deserialize_unit_struct(#type_name, __Visitor) + } } fn deserialize_tuple( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - variant_ident: Option, - impl_generics: &ast::Generics, - ty: P, + type_ident: &syn::Ident, + variant_ident: Option<&syn::Ident>, + impl_generics: &syn::Generics, + ty: syn::Ty, fields: &[Field], item_attrs: &attr::Item, -) -> P { +) -> Tokens { let where_clause = &impl_generics.where_clause; - let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor( - builder, - impl_generics, - ); + let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(impl_generics); let is_enum = variant_ident.is_some(); let type_path = match variant_ident { - Some(variant_ident) => builder.path().id(type_ident).id(variant_ident).build(), - None => builder.path().id(type_ident).build(), + Some(variant_ident) => quote!(#type_ident::#variant_ident), + None => quote!(#type_ident), }; let nfields = fields.len(); let visit_newtype_struct = if !is_enum && nfields == 1 { Some(deserialize_newtype_struct( - cx, - builder, type_ident, &type_path, impl_generics, @@ -316,90 +262,85 @@ fn deserialize_tuple( }; let visit_seq = deserialize_seq( - cx, - builder, type_ident, - type_path, + &type_path, impl_generics, fields, false, ); let dispatch = if is_enum { - quote_expr!(cx, - visitor.visit_tuple($nfields, $visitor_expr)) + quote!(visitor.visit_tuple(#nfields, #visitor_expr)) } else if nfields == 1 { - let type_name = name_expr(builder, item_attrs.name()); - quote_expr!(cx, - deserializer.deserialize_newtype_struct($type_name, $visitor_expr)) + let type_name = item_attrs.name().deserialize_name(); + quote!(deserializer.deserialize_newtype_struct(#type_name, #visitor_expr)) } else { - let type_name = name_expr(builder, item_attrs.name()); - quote_expr!(cx, - deserializer.deserialize_tuple_struct($type_name, $nfields, $visitor_expr)) + let type_name = item_attrs.name().deserialize_name(); + quote!(deserializer.deserialize_tuple_struct(#type_name, #nfields, #visitor_expr)) }; - quote_block!(cx, { - $visitor_item + quote!({ + #visitor_item - impl $impl_generics _serde::de::Visitor for $visitor_ty $where_clause { - type Value = $ty; + impl #impl_generics _serde::de::Visitor for #visitor_ty #where_clause { + type Value = #ty; - $visit_newtype_struct + #visit_newtype_struct #[inline] - fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error> + fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<#ty, __V::Error> where __V: _serde::de::SeqVisitor - $visit_seq + { + #visit_seq + } } - $dispatch - }).unwrap() + #dispatch + }) } fn deserialize_seq( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - type_path: ast::Path, - impl_generics: &ast::Generics, + type_ident: &syn::Ident, + type_path: &Tokens, + impl_generics: &syn::Generics, fields: &[Field], is_struct: bool, -) -> P { +) -> Tokens { let mut index_in_seq = 0usize; let let_values: Vec<_> = fields.iter() .enumerate() .map(|(i, field)| { - let name = builder.id(format!("__field{}", i)); + let name = aster::id(format!("__field{}", i)); if field.attrs.skip_deserializing() { - let default = expr_is_missing(cx, builder, &field.attrs); - quote_stmt!(cx, - let $name = $default; - ).unwrap() + let default = expr_is_missing(&field.attrs); + quote! { + let #name = #default; + } } else { let visit = match field.attrs.deserialize_with() { None => { let field_ty = &field.ty; - quote_expr!(cx, try!(visitor.visit::<$field_ty>())) + quote!(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) + type_ident, impl_generics, &field.ty, path); + quote!({ + #wrapper + #wrapper_impl + try!(visitor.visit::<#wrapper_ty>()).map(|wrap| wrap.value) }) } }; - let assign = quote_stmt!(cx, - let $name = match $visit { + let assign = quote! { + let #name = match #visit { Some(value) => { value }, None => { try!(visitor.end()); - return Err(_serde::de::Error::invalid_length($index_in_seq)); + return Err(_serde::de::Error::invalid_length(#index_in_seq)); } }; - ).unwrap(); + }; index_in_seq += 1; assign } @@ -407,110 +348,93 @@ fn deserialize_seq( .collect(); let result = if is_struct { - builder.expr().struct_path(type_path) - .with_id_exprs( - fields.iter() - .enumerate() - .map(|(i, field)| { - ( - match field.ident { - Some(name) => name.clone(), - None => { - cx.span_bug(field.span, "struct contains unnamed fields") - } - }, - builder.expr().id(format!("__field{}", i)), - ) - }) - ) - .build() + let args = fields.iter() + .enumerate() + .map(|(i, field)| { + let ident = field.ident.clone().expect("struct contains unnamed fields"); + let value = aster::id(format!("__field{}", i)); + quote!(#ident: #value) + }); + quote! { + #type_path { #(args),* } + } } else { - builder.expr().call() - .build_path(type_path) - .with_args((0..fields.len()).map(|i| builder.expr().id(format!("__field{}", i)))) - .build() + let args = (0..fields.len()).map(|i| aster::id(format!("__field{}", i))); + quote! { + #type_path ( #(args),* ) + } }; - quote_block!(cx, { - $let_values + quote! { + #(let_values)* try!(visitor.end()); - Ok($result) - }).unwrap() + Ok(#result) + } } fn deserialize_newtype_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - type_path: &ast::Path, - impl_generics: &ast::Generics, + type_ident: &syn::Ident, + type_path: &Tokens, + impl_generics: &syn::Generics, field: &Field, -) -> Vec { +) -> Tokens { let value = match field.attrs.deserialize_with() { None => { let field_ty = &field.ty; - quote_expr!(cx, - try!(<$field_ty as _serde::Deserialize>::deserialize(__e))) + quote! { + try!(<#field_ty as _serde::Deserialize>::deserialize(__e)) + } } 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!(<$wrapper_ty as _serde::Deserialize>::deserialize(__e)).value + type_ident, impl_generics, &field.ty, path); + quote!({ + #wrapper + #wrapper_impl + try!(<#wrapper_ty as _serde::Deserialize>::deserialize(__e)).value }) } }; - quote_tokens!(cx, + quote! { #[inline] fn visit_newtype_struct<__E>(&mut self, __e: &mut __E) -> ::std::result::Result where __E: _serde::de::Deserializer, { - Ok($type_path($value)) + Ok(#type_path(#value)) } - ) + } } fn deserialize_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - variant_ident: Option, - impl_generics: &ast::Generics, - ty: P, + type_ident: &syn::Ident, + variant_ident: Option<&syn::Ident>, + impl_generics: &syn::Generics, + ty: syn::Ty, fields: &[Field], item_attrs: &attr::Item, -) -> P { +) -> Tokens { let where_clause = &impl_generics.where_clause; - let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor( - builder, - impl_generics, - ); + let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(impl_generics); let type_path = match variant_ident { - Some(variant_ident) => builder.path().id(type_ident).id(variant_ident).build(), - None => builder.path().id(type_ident).build(), + Some(ref variant_ident) => quote!(#type_ident::#variant_ident), + None => quote!(#type_ident), }; let visit_seq = deserialize_seq( - cx, - builder, type_ident, - type_path.clone(), + &type_path, impl_generics, fields, true, ); let (field_visitor, fields_stmt, visit_map) = deserialize_struct_visitor( - cx, - builder, type_ident, - type_path.clone(), + type_path, impl_generics, fields, item_attrs, @@ -518,55 +442,57 @@ fn deserialize_struct( let is_enum = variant_ident.is_some(); let dispatch = if is_enum { - quote_expr!(cx, - visitor.visit_struct(FIELDS, $visitor_expr)) + quote! { + visitor.visit_struct(FIELDS, #visitor_expr) + } } else { - let type_name = name_expr(builder, item_attrs.name()); - quote_expr!(cx, - deserializer.deserialize_struct($type_name, FIELDS, $visitor_expr)) + let type_name = item_attrs.name().deserialize_name(); + quote! { + deserializer.deserialize_struct(#type_name, FIELDS, #visitor_expr) + } }; - quote_block!(cx, { - $field_visitor + quote!({ + #field_visitor - $visitor_item + #visitor_item - impl $impl_generics _serde::de::Visitor for $visitor_ty $where_clause { - type Value = $ty; + impl #impl_generics _serde::de::Visitor for #visitor_ty #where_clause { + type Value = #ty; #[inline] - fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error> + fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<#ty, __V::Error> where __V: _serde::de::SeqVisitor - $visit_seq + { + #visit_seq + } #[inline] - fn visit_map<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error> + fn visit_map<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<#ty, __V::Error> where __V: _serde::de::MapVisitor - $visit_map + { + #visit_map + } } - $fields_stmt + #fields_stmt - $dispatch - }).unwrap() + #dispatch + }) } fn deserialize_item_enum( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - impl_generics: &ast::Generics, - ty: P, + type_ident: &syn::Ident, + impl_generics: &syn::Generics, + ty: syn::Ty, variants: &[Variant], item_attrs: &attr::Item -) -> P { +) -> Tokens { let where_clause = &impl_generics.where_clause; - let type_name = name_expr(builder, item_attrs.name()); + let type_name = item_attrs.name().deserialize_name(); let variant_visitor = deserialize_field_visitor( - cx, - builder, variants.iter() .map(|variant| variant.attrs.name().deserialize_name()) .collect(), @@ -574,32 +500,27 @@ fn deserialize_item_enum( true, ); - let variants_expr = builder.expr().ref_().slice() - .with_exprs( - variants.iter().map(|variant| builder.expr().str(variant.ident)) - ) - .build(); + let variant_names = variants.iter().map(|variant| variant.ident.to_string()); - let variants_stmt = quote_stmt!(cx, - const VARIANTS: &'static [&'static str] = $variants_expr; - ).unwrap(); + let variants_stmt = quote! { + const VARIANTS: &'static [&'static str] = &[ #(variant_names),* ]; + }; let ignored_arm = if item_attrs.deny_unknown_fields() { None } else { - Some(quote_arm!(cx, __Field::__ignore => { Err(_serde::de::Error::end_of_stream()) })) + Some(quote! { + __Field::__ignore => { Err(_serde::de::Error::end_of_stream()) } + }) }; // Match arms to extract a variant from a string let mut variant_arms = vec![]; for (i, variant) in variants.iter().enumerate() { - let variant_name = builder.pat().path() - .id("__Field").id(format!("__field{}", i)) - .build(); + let variant_name = aster::id(format!("__field{}", i)); + let variant_name = quote!(__Field::#variant_name); let block = deserialize_variant( - cx, - builder, type_ident, impl_generics, ty.clone(), @@ -607,71 +528,64 @@ fn deserialize_item_enum( item_attrs, ); - let arm = quote_arm!(cx, $variant_name => $block); + let arm = quote! { + #variant_name => #block + }; variant_arms.push(arm); } variant_arms.extend(ignored_arm.into_iter()); - let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor( - builder, - impl_generics, - ); + let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(impl_generics); - quote_block!(cx, { - $variant_visitor + quote! { + #variant_visitor - $visitor_item + #visitor_item - impl $impl_generics _serde::de::EnumVisitor for $visitor_ty $where_clause { - type Value = $ty; + impl #impl_generics _serde::de::EnumVisitor for #visitor_ty #where_clause { + type Value = #ty; - fn visit<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error> + fn visit<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<#ty, __V::Error> where __V: _serde::de::VariantVisitor, { match try!(visitor.visit_variant()) { - $variant_arms + #(variant_arms)* } } } - $variants_stmt + #variants_stmt - deserializer.deserialize_enum($type_name, VARIANTS, $visitor_expr) - }).unwrap() + deserializer.deserialize_enum(#type_name, VARIANTS, #visitor_expr) + } } fn deserialize_variant( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - generics: &ast::Generics, - ty: P, + type_ident: &syn::Ident, + generics: &syn::Generics, + ty: syn::Ty, variant: &Variant, item_attrs: &attr::Item, -) -> P { - let variant_ident = variant.ident; +) -> Tokens { + let variant_ident = &variant.ident; match variant.style { Style::Unit => { - quote_block!(cx, { + quote!({ try!(visitor.visit_unit()); - Ok($type_ident::$variant_ident) - }).unwrap() + Ok(#type_ident::#variant_ident) + }) } Style::Newtype => { deserialize_newtype_variant( - cx, - builder, type_ident, - variant_ident, + &variant_ident, generics, &variant.fields[0], ) } Style::Tuple => { deserialize_tuple( - cx, - builder, type_ident, Some(variant_ident), generics, @@ -682,8 +596,6 @@ fn deserialize_variant( } Style::Struct => { deserialize_struct( - cx, - builder, type_ident, Some(variant_ident), generics, @@ -696,146 +608,142 @@ fn deserialize_variant( } fn deserialize_newtype_variant( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - variant_ident: Ident, - impl_generics: &ast::Generics, + type_ident: &syn::Ident, + variant_ident: &syn::Ident, + impl_generics: &syn::Generics, field: &Field, -) -> P { +) -> Tokens { let visit = match field.attrs.deserialize_with() { None => { let field_ty = &field.ty; - quote_expr!(cx, try!(visitor.visit_newtype::<$field_ty>())) + quote!(try!(visitor.visit_newtype::<#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_newtype::<$wrapper_ty>()).value + type_ident, impl_generics, &field.ty, path); + quote!({ + #wrapper + #wrapper_impl + try!(visitor.visit_newtype::<#wrapper_ty>()).value }) } }; - quote_block!(cx, { - Ok($type_ident::$variant_ident($visit)) - }).unwrap() + quote! { + Ok(#type_ident::#variant_ident(#visit)), + } } fn deserialize_field_visitor( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - field_names: Vec, + field_names: Vec, item_attrs: &attr::Item, is_variant: bool, -) -> Vec> { +) -> Tokens { // Create the field names for the fields. let field_idents: Vec<_> = (0 .. field_names.len()) - .map(|i| builder.id(format!("__field{}", i))) + .map(|i| aster::id(format!("__field{}", i))) .collect(); let ignore_variant = if item_attrs.deny_unknown_fields() { None } else { - let skip_ident = builder.id("__ignore"); - Some(builder.variant(skip_ident).unit()) + Some(quote!(__ignore,)) }; - let field_enum = builder.item() - .attr().allow(&["non_camel_case_types"]) - .enum_("__Field") - .with_variants( - field_idents.iter().map(|field_ident| { - builder.variant(field_ident).unit() - }) - ) - .with_variants(ignore_variant.into_iter()) - .build(); + let field_enum = quote! { + #[allow(non_camel_case_types)] + enum __Field { + #(field_idents,)* + #ignore_variant + } + }; let index_field_arms: Vec<_> = field_idents.iter() .enumerate() .map(|(field_index, field_ident)| { - quote_arm!(cx, $field_index => { Ok(__Field::$field_ident) }) + quote! { + #field_index => { Ok(__Field::#field_ident) } + } }) .collect(); let (index_error_msg, unknown_ident) = if is_variant { - (builder.expr().str("expected a variant"), builder.id("unknown_variant")) + ("expected a variant", aster::id("unknown_variant")) } else { - (builder.expr().str("expected a field"), builder.id("unknown_field")) + ("expected a field", aster::id("unknown_field")) }; let fallthrough_index_arm_expr = if !is_variant && !item_attrs.deny_unknown_fields() { - quote_expr!(cx, Ok(__Field::__ignore)) + quote! { + Ok(__Field::__ignore) + } } else { - quote_expr!(cx, Err(_serde::de::Error::invalid_value($index_error_msg))) + quote! { + Err(_serde::de::Error::invalid_value(#index_error_msg)) + } }; - let index_body = quote_block!(cx, { + let index_body = quote! { match value { - $index_field_arms - _ => $fallthrough_index_arm_expr + #(index_field_arms)* + _ => #fallthrough_index_arm_expr } - }).unwrap(); - - // Convert the field names into byte strings. - let str_field_names: Vec<_> = field_names.iter() - .map(|name| builder.expr().lit().str(&name)) - .collect(); + }; // Match arms to extract a field from a string - let str_field_arms: Vec<_> = field_idents.iter().zip(str_field_names.iter()) + let str_field_arms: Vec<_> = field_idents.iter().zip(field_names.iter()) .map(|(field_ident, field_name)| { - quote_arm!(cx, $field_name => { Ok(__Field::$field_ident) }) + quote! { + #field_name => { Ok(__Field::#field_ident) } + } }) .collect(); let fallthrough_str_arm_expr = if !is_variant && !item_attrs.deny_unknown_fields() { - quote_expr!(cx, Ok(__Field::__ignore)) + quote! { + Ok(__Field::__ignore) + } } else { - quote_expr!(cx, Err(_serde::de::Error::$unknown_ident(value))) + quote! { + Err(_serde::de::Error::#unknown_ident(value)) + } }; - let str_body = quote_block!(cx, { + let str_body = quote! { match value { - $str_field_arms - _ => $fallthrough_str_arm_expr + #(str_field_arms)* + _ => #fallthrough_str_arm_expr } - }).unwrap(); - - // Convert the field names into byte strings. - let bytes_field_names: Vec<_> = field_names.iter() - .map(|name| { - let name: &str = name; - builder.expr().lit().byte_str(name) - }) - .collect(); + }; // Match arms to extract a field from a string - let bytes_field_arms: Vec<_> = field_idents.iter().zip(bytes_field_names.iter()) + let bytes_field_arms: Vec<_> = field_idents.iter().zip(field_names.iter()) .map(|(field_ident, field_name)| { - quote_arm!(cx, $field_name => { Ok(__Field::$field_ident) }) + let bytes_field_name = quote::ByteStr(&field_name); + quote! { + #bytes_field_name => { Ok(__Field::#field_ident) } + } }) .collect(); let fallthrough_bytes_arm_expr = if !is_variant && !item_attrs.deny_unknown_fields() { - quote_expr!(cx, Ok(__Field::__ignore)) + quote! { + Ok(__Field::__ignore) + } } else { - quote_expr!(cx, { + quote!({ let value = ::std::string::String::from_utf8_lossy(value); - Err(_serde::de::Error::$unknown_ident(&value)) + Err(_serde::de::Error::#unknown_ident(&value)) }) }; - let bytes_body = quote_block!(cx, { + let bytes_body = quote! { match value { - $bytes_field_arms - _ => $fallthrough_bytes_arm_expr + #(bytes_field_arms)* + _ => #fallthrough_bytes_arm_expr } - }).unwrap(); + }; - let impl_item = quote_item!(cx, + let impl_item = quote! { impl _serde::de::Deserialize for __Field { #[inline] fn deserialize<__D>(deserializer: &mut __D) -> ::std::result::Result<__Field, __D::Error> @@ -848,49 +756,52 @@ fn deserialize_field_visitor( fn visit_usize<__E>(&mut self, value: usize) -> ::std::result::Result<__Field, __E> where __E: _serde::de::Error - $index_body + { + #index_body + } fn visit_str<__E>(&mut self, value: &str) -> ::std::result::Result<__Field, __E> where __E: _serde::de::Error - $str_body + { + #str_body + } fn visit_bytes<__E>(&mut self, value: &[u8]) -> ::std::result::Result<__Field, __E> where __E: _serde::de::Error - $bytes_body + { + #bytes_body + } } deserializer.deserialize_struct_field(__FieldVisitor) } } - ).unwrap(); + }; - vec![field_enum, impl_item] + quote! { + #field_enum + #impl_item + } } fn deserialize_struct_visitor( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - struct_path: ast::Path, - impl_generics: &ast::Generics, + type_ident: &syn::Ident, + struct_path: Tokens, + impl_generics: &syn::Generics, fields: &[Field], item_attrs: &attr::Item, -) -> (Vec>, ast::Stmt, P) { +) -> (Tokens, Tokens, Tokens) { let field_exprs = fields.iter() .map(|field| field.attrs.name().deserialize_name()) .collect(); let field_visitor = deserialize_field_visitor( - cx, - builder, field_exprs, item_attrs, false, ); let visit_map = deserialize_map( - cx, - builder, type_ident, struct_path, impl_generics, @@ -898,82 +809,73 @@ fn deserialize_struct_visitor( item_attrs, ); - let fields_expr = builder.expr().ref_().slice() - .with_exprs( - fields.iter() - .map(|field| { - match field.ident { - Some(name) => builder.expr().str(name), - None => { - cx.span_bug(field.span, "struct contains unnamed fields") - } - } - }) - ) - .build(); + let field_names = fields.iter().map(|field| { + field.ident.clone().expect("struct contains unnamed field").to_string() + }); - let fields_stmt = quote_stmt!(cx, - const FIELDS: &'static [&'static str] = $fields_expr; - ).unwrap(); + let fields_stmt = quote! { + const FIELDS: &'static [&'static str] = &[ #(field_names),* ]; + }; (field_visitor, fields_stmt, visit_map) } fn deserialize_map( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_ident: Ident, - struct_path: ast::Path, - impl_generics: &ast::Generics, + type_ident: &syn::Ident, + struct_path: Tokens, + impl_generics: &syn::Generics, fields: &[Field], item_attrs: &attr::Item, -) -> P { +) -> Tokens { // Create the field names for the fields. let fields_names = fields.iter() .enumerate() .map(|(i, field)| - (field, builder.id(format!("__field{}", i)))) + (field, aster::id(format!("__field{}", i)))) .collect::>(); // Declare each field that will be deserialized. - let let_values: Vec = fields_names.iter() + let let_values: Vec<_> = fields_names.iter() .filter(|&&(field, _)| !field.attrs.skip_deserializing()) - .map(|&(field, name)| { + .map(|&(field, ref name)| { let field_ty = &field.ty; - quote_stmt!(cx, let mut $name: Option<$field_ty> = None;).unwrap() + quote! { + let mut #name: Option<#field_ty> = None; + } }) .collect(); // Match arms to extract a value for a field. let value_arms = fields_names.iter() .filter(|&&(field, _)| !field.attrs.skip_deserializing()) - .map(|&(ref field, name)| { + .map(|&(ref field, ref name)| { let deser_name = field.attrs.name().deserialize_name(); - let name_str = builder.expr().lit().str(deser_name); let visit = match field.attrs.deserialize_with() { None => { let field_ty = &field.ty; - quote_expr!(cx, try!(visitor.visit_value::<$field_ty>())) + quote! { + 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 - })) + type_ident, impl_generics, &field.ty, path); + quote!({ + #wrapper + #wrapper_impl + try!(visitor.visit_value::<#wrapper_ty>()).value + }) } }; - quote_arm!(cx, - __Field::$name => { - if $name.is_some() { - return Err(<__V::Error as _serde::de::Error>::duplicate_field($name_str)); + quote! { + __Field::#name => { + if #name.is_some() { + return Err(<__V::Error as _serde::de::Error>::duplicate_field(#deser_name)); } - $name = Some($visit); + #name = Some(#visit); } - ) + } }) .collect::>(); @@ -981,12 +883,12 @@ fn deserialize_map( // Ignored even if `deny_unknown_fields` is set. let skipped_arms = fields_names.iter() .filter(|&&(field, _)| field.attrs.skip_deserializing()) - .map(|&(_, name)| { - quote_arm!(cx, - __Field::$name => { + .map(|&(_, ref name)| { + quote! { + __Field::#name => { try!(visitor.visit_value::<_serde::de::impls::IgnoredAny>()); } - ) + } }) .collect::>(); @@ -994,78 +896,66 @@ fn deserialize_map( let ignored_arm = if item_attrs.deny_unknown_fields() { None } else { - Some(quote_arm!(cx, + Some(quote! { _ => { try!(visitor.visit_value::<_serde::de::impls::IgnoredAny>()); } - )) + }) }; let extract_values = fields_names.iter() .filter(|&&(field, _)| !field.attrs.skip_deserializing()) - .map(|&(field, name)| { - let missing_expr = expr_is_missing(cx, builder, &field.attrs); + .map(|&(field, ref name)| { + let missing_expr = expr_is_missing(&field.attrs); - quote_stmt!(cx, - let $name = match $name { - Some($name) => $name, - None => $missing_expr + quote! { + let #name = match #name { + Some(#name) => #name, + None => #missing_expr }; - ).unwrap() + } }) .collect::>(); - let result = builder.expr().struct_path(struct_path) - .with_id_exprs( - fields_names.iter() - .map(|&(field, name)| { - ( - match field.ident { - Some(name) => name.clone(), - None => { - cx.span_bug(field.span, "struct contains unnamed fields") - } - }, - if field.attrs.skip_deserializing() { - expr_is_missing(cx, builder, &field.attrs) - } else { - builder.expr().id(name) - } - ) - }) - ) - .build(); + let result = fields_names.iter() + .map(|&(field, ref name)| { + let ident = field.ident.clone().expect("struct contains unnamed fields"); + let value = if field.attrs.skip_deserializing() { + expr_is_missing(&field.attrs) + } else { + quote!(#name) + }; + quote!(#ident: #value) + }); - quote_block!(cx, { - $let_values + quote! { + #(let_values)* while let Some(key) = try!(visitor.visit_key::<__Field>()) { match key { - $value_arms - $skipped_arms - $ignored_arm + #(value_arms)* + #(skipped_arms)* + #ignored_arm } } try!(visitor.end()); - $extract_values + #(extract_values)* - Ok($result) - }).unwrap() + Ok(#struct_path { #(result),* }) + } } /// 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) { + type_ident: &syn::Ident, + impl_generics: &syn::Generics, + field_ty: &syn::Ty, + deserialize_with: &syn::Path, +) -> (Tokens, Tokens, syn::Path) { // Quasi-quoting doesn't do a great job of expanding generics into paths, // so manually build it. - let wrapper_ty = builder.path() + let wrapper_ty = aster::path() .segment("__SerdeDeserializeWithStruct") .with_generics(impl_generics.clone()) .build() @@ -1073,97 +963,82 @@ fn wrap_deserialize_with( let where_clause = &impl_generics.where_clause; - let phantom_ty = builder.path() + let phantom_ty = aster::path() .segment(type_ident) - .with_generics(builder.from_generics(impl_generics.clone()) + .with_generics(aster::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>, + quote! { + 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 { + }, + quote! { + impl #impl_generics _serde::de::Deserialize for #wrapper_ty #where_clause { fn deserialize<__D>(__d: &mut __D) -> ::std::result::Result where __D: _serde::de::Deserializer { - let value = try!($deserialize_with(__d)); + let value = try!(#deserialize_with(__d)); Ok(__SerdeDeserializeWithStruct { value: value, phantom: ::std::marker::PhantomData, }) } } - ).unwrap(), + }, wrapper_ty, ) } -fn expr_is_missing( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - attrs: &attr::Field, -) -> P { +fn expr_is_missing(attrs: &attr::Field) -> Tokens { match *attrs.default() { attr::FieldDefault::Default => { - return quote_expr!(cx, ::std::default::Default::default()); + return quote!(::std::default::Default::default()); } attr::FieldDefault::Path(ref path) => { - return quote_expr!(cx, $path()); + return quote!(#path()); } attr::FieldDefault::None => { /* below */ } } - let name = name_expr(builder, attrs.name()); + let name = attrs.name().deserialize_name(); match attrs.deserialize_with() { None => { - quote_expr!(cx, try!(visitor.missing_field($name))) + quote! { + try!(visitor.missing_field(#name)) + } } Some(_) => { - quote_expr!(cx, return Err( - <__V::Error as _serde::de::Error>::missing_field($name))) + quote! { + return Err(<__V::Error as _serde::de::Error>::missing_field(#name)) + } } } } -fn name_expr( - builder: &aster::AstBuilder, - name: &attr::Name, -) -> P { - builder.expr().str(name.deserialize_name()) -} - -fn check_no_str( - cx: &ExtCtxt, - item: &Item, -) -> Result<(), ()> { - let fail = |field: &Field| { - cx.span_err( - field.span, +fn check_no_str(cx: &internals::Ctxt, item: &Item) { + let fail = || { + cx.error( "Serde does not support deserializing fields of type &str; \ consider using String instead"); - Err(()) }; for field in item.body.all_fields() { if field.attrs.skip_deserializing() || field.attrs.deserialize_with().is_some() { continue } - if let ast::TyKind::Rptr(_, ref inner) = field.ty.node { - if let ast::TyKind::Path(_, ref path) = inner.ty.node { - if path.segments.len() == 1 - && path.segments[0].identifier.name.as_str() == "str" - { - return fail(field); + if let syn::Ty::Rptr(_, ref inner) = *field.ty { + if let syn::Ty::Path(_, ref path) = inner.ty { + if path.segments.len() == 1 && path.segments[0].ident == "str" { + fail(); + return; } } } } - Ok(()) } diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index 7de4db6d4..d3f0cab1f 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "1000"] + #![cfg_attr(feature = "clippy", plugin(clippy))] #![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "clippy", allow(too_many_arguments))] @@ -31,6 +33,7 @@ use std::path::Path; use syntax::feature_gate::AttributeType; mod bound; +mod de; mod ser; #[cfg(feature = "with-syntex")] @@ -67,7 +70,7 @@ fn syntex_registry() -> syntex::Registry { reg.add_attr("feature(custom_attribute)"); reg.add_decorator("derive_Serialize", expand_derive_serialize); - reg.add_decorator("derive_Deserialize", de::expand_derive_deserialize); + reg.add_decorator("derive_Deserialize", expand_derive_deserialize); reg.add_post_expansion_pass(strip_attributes); @@ -107,53 +110,56 @@ pub fn register(reg: &mut rustc_plugin::Registry) { syntax::ext::base::MultiDecorator( Box::new(expand_derive_serialize))); - /*reg.register_syntax_extension( + reg.register_syntax_extension( syntax::parse::token::intern("derive_Deserialize"), syntax::ext::base::MultiDecorator( - Box::new(de::expand_derive_deserialize)));*/ + Box::new(expand_derive_deserialize))); reg.register_attribute("serde".to_owned(), AttributeType::Normal); } -#[cfg(any(feature = "with-syntex", feature = "with-libsyntax"))] -use syntax::ast::MetaItem; -#[cfg(any(feature = "with-syntex", feature = "with-libsyntax"))] -use syntax::codemap::Span; -#[cfg(any(feature = "with-syntex", feature = "with-libsyntax"))] -use syntax::ext::base::{Annotatable, ExtCtxt}; - -#[cfg(any(feature = "with-syntex", feature = "with-libsyntax"))] -fn expand_derive_serialize( - cx: &mut ExtCtxt, - _span: Span, - meta_item: &MetaItem, - annotatable: &Annotatable, - push: &mut FnMut(Annotatable) -) { - let item = match *annotatable { - Annotatable::Item(ref item) => item, - _ => { - cx.span_err( - meta_item.span, - "`#[derive(Serialize)]` may only be applied to structs and enums"); - return; - } - }; +macro_rules! shim { + ($name:ident $pkg:ident :: $func:ident) => { + fn $func( + cx: &mut ::syntax::ext::base::ExtCtxt, + _span: ::syntax::codemap::Span, + meta_item: &::syntax::ast::MetaItem, + annotatable: &::syntax::ext::base::Annotatable, + push: &mut FnMut(::syntax::ext::base::Annotatable) + ) { + let item = match *annotatable { + ::syntax::ext::base::Annotatable::Item(ref item) => item, + _ => { + cx.span_err( + meta_item.span, + concat!("`#[derive(", + stringify!($name), + ")]` may only be applied to structs and enums")); + return; + } + }; - use syntax::print::pprust; - let s = pprust::item_to_string(item); + use syntax::print::pprust; + let s = pprust::item_to_string(item); - let syn_item = syn::parse_item(&s).unwrap(); - let expanded = ser::expand_derive_serialize(&syn_item).to_string(); + let syn_item = syn::parse_item(&s).unwrap(); + let expanded = $pkg::$func(&syn_item).to_string(); - use syntax::parse; - let name = "Serialize".to_string(); - let cfg = Vec::new(); - let sess = parse::ParseSess::new(); - let impl_item = parse::parse_item_from_source_str(name, expanded, cfg, &sess); - push(Annotatable::Item(impl_item.unwrap().unwrap())); + use syntax::parse; + let name = stringify!($name).to_string(); + let cfg = Vec::new(); + let sess = parse::ParseSess::new(); + let impl_item = parse::parse_item_from_source_str(name, expanded, cfg, &sess); + push(::syntax::ext::base::Annotatable::Item(impl_item.unwrap().unwrap())); + } + }; } +#[cfg(any(feature = "with-syntex", feature = "with-libsyntax"))] +shim!(Serialize ser::expand_derive_serialize); +#[cfg(any(feature = "with-syntex", feature = "with-libsyntax"))] +shim!(Deserialize de::expand_derive_deserialize); + #[cfg(feature = "with-syn")] pub fn expand_single_item(item: &str) -> String { let syn_item = syn::parse_item(item).unwrap(); @@ -164,7 +170,7 @@ pub fn expand_single_item(item: &str) -> String { None }; let expanded_de = if de { - unimplemented!() + Some(de::expand_derive_deserialize(&syn_item)) } else { None:: };