From c34baa1e5f219316212ec4dfdcb5b4d6f18473ea Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 10 Sep 2016 21:53:14 -0700 Subject: [PATCH 01/15] No more syntex for serde_derive --- serde_codegen/Cargo.toml | 20 +- serde_codegen/build.rs | 28 -- serde_codegen/src/bound.rs | 56 ++- serde_codegen/src/lib.rs | 183 ++++++++- serde_codegen/src/lib.rs.in | 4 - serde_codegen/src/ser.rs | 531 +++++++++++---------------- serde_codegen/src/span.rs | 43 --- serde_codegen_internals/Cargo.toml | 5 +- serde_codegen_internals/src/ast.rs | 84 ++--- serde_codegen_internals/src/attr.rs | 440 +++++++--------------- serde_codegen_internals/src/ctxt.rs | 29 ++ serde_codegen_internals/src/error.rs | 19 - serde_codegen_internals/src/lib.rs | 17 +- serde_derive/Cargo.toml | 7 +- serde_derive/src/lib.rs | 4 +- 15 files changed, 611 insertions(+), 859 deletions(-) delete mode 100644 serde_codegen/build.rs delete mode 100644 serde_codegen/src/lib.rs.in delete mode 100644 serde_codegen/src/span.rs create mode 100644 serde_codegen_internals/src/ctxt.rs delete mode 100644 serde_codegen_internals/src/error.rs diff --git a/serde_codegen/Cargo.toml b/serde_codegen/Cargo.toml index 703575e7..9fa605c4 100644 --- a/serde_codegen/Cargo.toml +++ b/serde_codegen/Cargo.toml @@ -8,31 +8,23 @@ homepage = "https://serde.rs" repository = "https://github.com/serde-rs/serde" documentation = "https://serde.rs/codegen.html" keywords = ["serde", "serialization"] -build = "build.rs" -include = ["Cargo.toml", "build.rs", "src/**/*.rs", "src/lib.rs.in"] +include = ["Cargo.toml", "src/**/*.rs"] [features] default = ["with-syntex"] -unstable = ["quasi_macros"] +unstable = [] unstable-testing = ["clippy"] with-syntex = [ - "quasi/with-syntex", - "quasi_codegen", - "quasi_codegen/with-syntex", - "serde_codegen_internals/with-syntex", "syntex", "syntex_syntax", ] - -[build-dependencies] -quasi_codegen = { version = "^0.19.0", optional = true } -syntex = { version = "^0.43.0", optional = true } +with-libsyntax = [] +with-syn = [] [dependencies] -aster = { version = "^0.26.0", default-features = false } clippy = { version = "^0.*", optional = true } -quasi = { version = "^0.19.0", default-features = false } -quasi_macros = { version = "^0.19.0", optional = true } +quote = "0.1" serde_codegen_internals = { version = "=0.8.0", default-features = false, path = "../serde_codegen_internals" } +syn = { version = "0.5", features = ["aster", "visit"] } syntex = { version = "^0.43.0", optional = true } syntex_syntax = { version = "^0.43.0", optional = true } diff --git a/serde_codegen/build.rs b/serde_codegen/build.rs deleted file mode 100644 index 9ff828f2..00000000 --- a/serde_codegen/build.rs +++ /dev/null @@ -1,28 +0,0 @@ -#[cfg(feature = "with-syntex")] -mod inner { - extern crate quasi_codegen; - - use std::env; - use std::path::Path; - use std::thread::spawn; - - pub fn main() { - // put everything into a thread, so users can use `RUST_MIN_STACK` to increase the amount of stack - spawn(|| { - let out_dir = env::var_os("OUT_DIR").unwrap(); - - let src = Path::new("src/lib.rs.in"); - let dst = Path::new(&out_dir).join("lib.rs"); - quasi_codegen::expand(&src, &dst).unwrap(); - }).join().unwrap() - } -} - -#[cfg(not(feature = "with-syntex"))] -mod inner { - pub fn main() {} -} - -fn main() { - inner::main(); -} diff --git a/serde_codegen/src/bound.rs b/serde_codegen/src/bound.rs index 7bd5ddc6..fef77ba3 100644 --- a/serde_codegen/src/bound.rs +++ b/serde_codegen/src/bound.rs @@ -1,9 +1,6 @@ use std::collections::HashSet; -use aster::AstBuilder; - -use syntax::ast; -use syntax::visit; +use syn::{self, aster, visit}; use internals::ast::Item; use internals::attr; @@ -11,10 +8,10 @@ use internals::attr; // Remove the default from every type parameter because in the generated impls // they look like associated types: "error: associated type bindings are not // allowed here". -pub fn without_defaults(generics: &ast::Generics) -> ast::Generics { - ast::Generics { +pub fn without_defaults(generics: &syn::Generics) -> syn::Generics { + syn::Generics { ty_params: generics.ty_params.iter().map(|ty_param| { - ast::TyParam { + syn::TyParam { default: None, .. ty_param.clone() }}).collect(), @@ -23,24 +20,22 @@ pub fn without_defaults(generics: &ast::Generics) -> ast::Generics { } pub fn with_where_predicates( - builder: &AstBuilder, - generics: &ast::Generics, - predicates: &[ast::WherePredicate], -) -> ast::Generics { - builder.from_generics(generics.clone()) + generics: &syn::Generics, + predicates: &[syn::WherePredicate], +) -> syn::Generics { + aster::from_generics(generics.clone()) .with_predicates(predicates.to_vec()) .build() } pub fn with_where_predicates_from_fields( - builder: &AstBuilder, item: &Item, - generics: &ast::Generics, + generics: &syn::Generics, from_field: F, -) -> ast::Generics - where F: Fn(&attr::Field) -> Option<&[ast::WherePredicate]>, +) -> syn::Generics + where F: Fn(&attr::Field) -> Option<&[syn::WherePredicate]>, { - builder.from_generics(generics.clone()) + aster::from_generics(generics.clone()) .with_predicates( item.body.all_fields() .flat_map(|field| from_field(&field.attrs)) @@ -60,34 +55,33 @@ pub fn with_where_predicates_from_fields( // c: C, // } pub fn with_bound( - builder: &AstBuilder, item: &Item, - generics: &ast::Generics, + generics: &syn::Generics, filter: F, - bound: &ast::Path, -) -> ast::Generics + bound: &syn::Path, +) -> syn::Generics where F: Fn(&attr::Field) -> bool, { struct FindTyParams { // Set of all generic type parameters on the current struct (A, B, C in // the example). Initialized up front. - all_ty_params: HashSet, + all_ty_params: HashSet, // Set of generic type parameters used in fields for which filter // returns true (A and B in the example). Filled in as the visitor sees // them. - relevant_ty_params: HashSet, + relevant_ty_params: HashSet, } impl visit::Visitor for FindTyParams { - fn visit_path(&mut self, path: &ast::Path, _id: ast::NodeId) { + fn visit_path(&mut self, path: &syn::Path) { if let Some(seg) = path.segments.last() { - if seg.identifier.name.as_str() == "PhantomData" { + if seg.ident == "PhantomData" { // Hardcoded exception, because PhantomData implements // Serialize and Deserialize whether or not T implements it. return; } } if !path.global && path.segments.len() == 1 { - let id = path.segments[0].identifier.name; + let id = path.segments[0].ident.clone(); if self.all_ty_params.contains(&id) { self.relevant_ty_params.insert(id); } @@ -97,7 +91,7 @@ pub fn with_bound( } let all_ty_params: HashSet<_> = generics.ty_params.iter() - .map(|ty_param| ty_param.ident.name) + .map(|ty_param| ty_param.ident.clone()) .collect(); let relevant_tys = item.body.all_fields() @@ -112,14 +106,14 @@ pub fn with_bound( visit::walk_ty(&mut visitor, ty); } - builder.from_generics(generics.clone()) + aster::from_generics(generics.clone()) .with_predicates( generics.ty_params.iter() - .map(|ty_param| ty_param.ident.name) + .map(|ty_param| ty_param.ident.clone()) .filter(|id| visitor.relevant_ty_params.contains(id)) - .map(|id| builder.where_predicate() + .map(|id| aster::where_predicate() // the type parameter that is being bounded e.g. T - .bound().build(builder.ty().id(id)) + .bound().build(aster::ty().id(id)) // the bound e.g. Serialize .bound().trait_(bound.clone()).build() .build())) diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index 55318af6..7de4db6d 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -2,11 +2,8 @@ #![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "clippy", allow(too_many_arguments))] #![cfg_attr(feature = "clippy", allow(used_underscore_binding))] -#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))] -#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))] +#![cfg_attr(feature = "with-libsyntax", feature(rustc_private, plugin))] -extern crate aster; -extern crate quasi; extern crate serde_codegen_internals as internals; #[cfg(feature = "with-syntex")] @@ -16,24 +13,25 @@ extern crate syntex; #[macro_use] extern crate syntex_syntax as syntax; -#[cfg(not(feature = "with-syntex"))] +#[cfg(feature = "with-libsyntax")] #[macro_use] extern crate syntax; -#[cfg(not(feature = "with-syntex"))] +#[cfg(feature = "with-libsyntax")] extern crate rustc_plugin; +extern crate syn; +#[macro_use] +extern crate quote; + #[cfg(feature = "with-syntex")] use std::path::Path; -#[cfg(not(feature = "with-syntex"))] +#[cfg(feature = "with-libsyntax")] use syntax::feature_gate::AttributeType; -#[cfg(feature = "with-syntex")] -include!(concat!(env!("OUT_DIR"), "/lib.rs")); - -#[cfg(not(feature = "with-syntex"))] -include!("lib.rs.in"); +mod bound; +mod ser; #[cfg(feature = "with-syntex")] fn syntex_registry() -> syntex::Registry { @@ -68,7 +66,7 @@ fn syntex_registry() -> syntex::Registry { reg.add_attr("feature(custom_derive)"); reg.add_attr("feature(custom_attribute)"); - reg.add_decorator("derive_Serialize", ser::expand_derive_serialize); + reg.add_decorator("derive_Serialize", expand_derive_serialize); reg.add_decorator("derive_Deserialize", de::expand_derive_deserialize); reg.add_post_expansion_pass(strip_attributes); @@ -102,17 +100,168 @@ pub fn expand(src: S, dst: D) -> Result<(), syntex::Error> syntex::with_extra_stack(expand_thread) } -#[cfg(not(feature = "with-syntex"))] +#[cfg(feature = "with-libsyntax")] pub fn register(reg: &mut rustc_plugin::Registry) { reg.register_syntax_extension( syntax::parse::token::intern("derive_Serialize"), syntax::ext::base::MultiDecorator( - Box::new(ser::expand_derive_serialize))); + 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(de::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; + } + }; + + 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(); + + 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())); +} + +#[cfg(feature = "with-syn")] +pub fn expand_single_item(item: &str) -> String { + let syn_item = syn::parse_item(item).unwrap(); + let (ser, de, syn_item) = strip_serde_derives(syn_item); + let expanded_ser = if ser { + Some(ser::expand_derive_serialize(&syn_item)) + } else { + None + }; + let expanded_de = if de { + unimplemented!() + } else { + None:: + }; + let syn_item = strip_serde_attrs(syn_item); + return quote!(#expanded_ser #expanded_de #syn_item).to_string(); + + fn strip_serde_derives(item: syn::Item) -> (bool, bool, syn::Item) { + let mut ser = false; + let mut de = false; + let item = syn::Item { + attrs: item.attrs.into_iter().flat_map(|attr| { + if attr.is_sugared_doc { + return Some(attr); + } + let (name, nested) = match attr.value { + syn::MetaItem::List(name, nested) => (name, nested), + _ => return Some(attr) + }; + if name != "derive" { + return Some(syn::Attribute { + value: syn::MetaItem::List(name, nested), + is_sugared_doc: false, + }); + } + let rest: Vec<_> = nested.into_iter().filter(|nested| { + match *nested { + syn::MetaItem::Word(ref word) if word == "Serialize" => { + ser = true; + false + } + syn::MetaItem::Word(ref word) if word == "Deserialize" => { + de = true; + false + } + _ => true, + } + }).collect(); + if rest.is_empty() { + None + } else { + Some(syn::Attribute { + value: syn::MetaItem::List(name, rest), + is_sugared_doc: false, + }) + } + }).collect(), + ..item + }; + (ser, de, item) + } + + fn strip_serde_attrs(item: syn::Item) -> syn::Item { + syn::Item { + attrs: strip_serde_from_attrs(item.attrs), + body: match item.body { + syn::Body::Enum(variants) => syn::Body::Enum( + variants.into_iter().map(|variant| { + syn::Variant { + ident: variant.ident, + attrs: strip_serde_from_attrs(variant.attrs), + data: strip_serde_from_variant_data(variant.data), + } + }).collect() + ), + syn::Body::Struct(variant_data) => syn::Body::Struct( + strip_serde_from_variant_data(variant_data) + ), + }, + ..item + } + } + + fn strip_serde_from_variant_data(data: syn::VariantData) -> syn::VariantData { + match data { + syn::VariantData::Struct(fields) => syn::VariantData::Struct( + fields.into_iter().map(strip_serde_from_field).collect() + ), + syn::VariantData::Tuple(fields) => syn::VariantData::Tuple( + fields.into_iter().map(strip_serde_from_field).collect() + ), + syn::VariantData::Unit => syn::VariantData::Unit, + } + } + + fn strip_serde_from_field(field: syn::Field) -> syn::Field { + syn::Field { + attrs: strip_serde_from_attrs(field.attrs), + ..field + } + } + + fn strip_serde_from_attrs(attrs: Vec) -> Vec { + attrs.into_iter().filter(|attr| { + match attr.value { + syn::MetaItem::List(ref ident, _) => ident != "serde", + _ => true, + } + }).collect() + } +} diff --git a/serde_codegen/src/lib.rs.in b/serde_codegen/src/lib.rs.in deleted file mode 100644 index 08dd5d3c..00000000 --- a/serde_codegen/src/lib.rs.in +++ /dev/null @@ -1,4 +0,0 @@ -mod bound; -mod de; -mod ser; -mod span; diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index d4883e8e..448c974c 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -1,103 +1,60 @@ -use aster; - -use syntax::ast::{self, Ident, MetaItem}; -use syntax::codemap::Span; -use syntax::ext::base::{Annotatable, ExtCtxt}; -use syntax::ptr::P; -use syntax::tokenstream::TokenTree; +use syn::{self, aster}; +use quote::Tokens; use bound; -use span; use internals::ast::{Body, Field, Item, Style, Variant}; -use internals::{attr, Error}; +use internals::{self, attr}; -pub 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; - } - }; +pub fn expand_derive_serialize(item: &syn::Item) -> Tokens { + let item = Item::from_ast(&internals::Ctxt::new(), item); - let item = match Item::from_ast(cx, item) { - Ok(item) => item, - Err(Error::UnexpectedItemKind) => { - cx.span_err(item.span, - "`#[derive(Serialize)]` may only be applied to structs and enums"); - return; - } - }; + let impl_generics = build_impl_generics(&item); - let builder = aster::AstBuilder::new().span(span); - - let impl_item = serialize_item(cx, &builder, &item); - push(span::record_expansion(cx, impl_item, "Serialize")) -} - -fn serialize_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 = serialize_body(cx, - builder, - &item, + let body = serialize_body(&item, &impl_generics, ty.clone()); let where_clause = &impl_generics.where_clause; - let dummy_const = builder.id(format!("_IMPL_SERIALIZE_FOR_{}", item.ident)); + let dummy_const = aster::id(format!("_IMPL_SERIALIZE_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::ser::Serialize for $ty $where_clause { + impl #impl_generics _serde::ser::Serialize for #ty #where_clause { fn serialize<__S>(&self, _serializer: &mut __S) -> ::std::result::Result<(), __S::Error> where __S: _serde::ser::Serializer - $body + { + #body + } } }; - ).unwrap() + } } // All the generics in the input, plus a bound `T: Serialize` for each generic // field type that will be serialized by us. -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.ser_bound()); match item.attrs.ser_bound() { Some(predicates) => { - bound::with_where_predicates(builder, &generics, predicates) + bound::with_where_predicates(&generics, predicates) } None => { - bound::with_bound(builder, item, &generics, + bound::with_bound(item, &generics, needs_serialize_bound, - &builder.path().ids(&["_serde", "ser", "Serialize"]).build()) + &aster::path().ids(&["_serde", "ser", "Serialize"]).build()) } } } @@ -113,18 +70,14 @@ fn needs_serialize_bound(attrs: &attr::Field) -> bool { } fn serialize_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) => { serialize_item_enum( - cx, - builder, - item.ident, + &item.ident, impl_generics, ty, variants, @@ -132,12 +85,10 @@ fn serialize_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"); } serialize_struct( - cx, - builder, impl_generics, ty, fields, @@ -145,12 +96,10 @@ fn serialize_body( } Body::Struct(Style::Tuple, 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"); } serialize_tuple_struct( - cx, - builder, impl_generics, ty, fields, @@ -158,8 +107,6 @@ fn serialize_body( } Body::Struct(Style::Newtype, ref fields) => { serialize_newtype_struct( - cx, - builder, impl_generics, ty, &fields[0], @@ -167,136 +114,116 @@ fn serialize_body( } Body::Struct(Style::Unit, _) => { serialize_unit_struct( - cx, - builder, &item.attrs) } } } -fn serialize_unit_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - item_attrs: &attr::Item, -) -> P { - let type_name = name_expr(builder, item_attrs.name()); +fn serialize_unit_struct(item_attrs: &attr::Item) -> Tokens { + let type_name = item_attrs.name().serialize_name(); - quote_block!(cx, { - _serializer.serialize_unit_struct($type_name) - }).unwrap() + quote! { + _serializer.serialize_unit_struct(#type_name) + } } fn serialize_newtype_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - impl_generics: &ast::Generics, - item_ty: P, + impl_generics: &syn::Generics, + item_ty: syn::Ty, field: &Field, item_attrs: &attr::Item, -) -> P { - let type_name = name_expr(builder, item_attrs.name()); +) -> Tokens { + let type_name = item_attrs.name().serialize_name(); - let mut field_expr = quote_expr!(cx, &self.0); + let mut field_expr = quote!(&self.0); if let Some(path) = field.attrs.serialize_with() { - field_expr = wrap_serialize_with(cx, builder, + field_expr = wrap_serialize_with( &item_ty, impl_generics, &field.ty, path, field_expr); } - quote_block!(cx, { - _serializer.serialize_newtype_struct($type_name, $field_expr) - }).unwrap() + quote! { + _serializer.serialize_newtype_struct(#type_name, #field_expr) + } } fn serialize_tuple_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - impl_generics: &ast::Generics, - ty: P, + impl_generics: &syn::Generics, + ty: syn::Ty, fields: &[Field], item_attrs: &attr::Item, -) -> P { +) -> Tokens { let serialize_stmts = serialize_tuple_struct_visitor( - cx, - builder, ty.clone(), fields, impl_generics, false, - cx.ident_of("serialize_tuple_struct_elt"), + aster::id("serialize_tuple_struct_elt"), ); - let type_name = name_expr(builder, item_attrs.name()); + let type_name = item_attrs.name().serialize_name(); let len = serialize_stmts.len(); - let let_mut = mut_if(cx, len > 0); + let let_mut = mut_if(len > 0); - quote_block!(cx, { - let $let_mut state = try!(_serializer.serialize_tuple_struct($type_name, $len)); - $serialize_stmts + quote! { + let #let_mut state = try!(_serializer.serialize_tuple_struct(#type_name, #len)); + #(serialize_stmts)* _serializer.serialize_tuple_struct_end(state) - }).unwrap() + } } fn serialize_struct( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - impl_generics: &ast::Generics, - ty: P, + impl_generics: &syn::Generics, + ty: syn::Ty, fields: &[Field], item_attrs: &attr::Item, -) -> P { +) -> Tokens { let serialize_fields = serialize_struct_visitor( - cx, - builder, ty.clone(), fields, impl_generics, false, - cx.ident_of("serialize_struct_elt"), + aster::id("serialize_struct_elt"), ); - let type_name = name_expr(builder, item_attrs.name()); + let type_name = item_attrs.name().serialize_name(); let mut serialized_fields = fields.iter() .filter(|&field| !field.attrs.skip_serializing()) .peekable(); - let let_mut = mut_if(cx, serialized_fields.peek().is_some()); + let let_mut = mut_if(serialized_fields.peek().is_some()); let len = serialized_fields .map(|field| { - let ident = field.ident.expect("struct has unnamed fields"); - let field_expr = quote_expr!(cx, &self.$ident); + let ident = field.ident.clone().expect("struct has unnamed fields"); + let field_expr = quote!(&self.#ident); match field.attrs.skip_serializing_if() { - Some(path) => quote_expr!(cx, if $path($field_expr) { 0 } else { 1 }), - None => quote_expr!(cx, 1), + Some(path) => quote!(if #path(#field_expr) { 0 } else { 1 }), + None => quote!(1), } }) - .fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr)); + .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); - quote_block!(cx, { - let $let_mut state = try!(_serializer.serialize_struct($type_name, $len)); - $serialize_fields + quote! { + let #let_mut state = try!(_serializer.serialize_struct(#type_name, #len)); + #(serialize_fields)* _serializer.serialize_struct_end(state) - }).unwrap() + } } fn serialize_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 arms: Vec<_> = variants.iter() .enumerate() .map(|(variant_index, variant)| { serialize_variant( - cx, - builder, type_ident, impl_generics, ty.clone(), @@ -307,44 +234,40 @@ fn serialize_item_enum( }) .collect(); - quote_block!(cx, { + quote! { match *self { - $arms + #(arms)* } - }).unwrap() + } } fn serialize_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, variant_index: usize, item_attrs: &attr::Item, -) -> ast::Arm { - let type_name = name_expr(builder, item_attrs.name()); +) -> Tokens { + let type_name = item_attrs.name().serialize_name(); - let variant_ident = variant.ident; - let variant_name = name_expr(builder, variant.attrs.name()); + let variant_ident = variant.ident.clone(); + let variant_name = variant.attrs.name().serialize_name(); match variant.style { Style::Unit => { - quote_arm!(cx, - $type_ident::$variant_ident => + quote! { + #type_ident::#variant_ident => _serde::ser::Serializer::serialize_unit_variant( _serializer, - $type_name, - $variant_index, - $variant_name, + #type_name, + #variant_index, + #variant_name, ), - ) + } }, Style::Newtype => { let block = serialize_newtype_variant( - cx, - builder, type_name, variant_index, variant_name, @@ -353,26 +276,21 @@ fn serialize_variant( &variant.fields[0], ); - quote_arm!(cx, - $type_ident::$variant_ident(ref __simple_value) => $block - ) + quote! { + #type_ident::#variant_ident(ref __simple_value) => #block, + } }, Style::Tuple => { - let field_names: Vec = (0 .. variant.fields.len()) - .map(|i| builder.id(format!("__field{}", i))) + let field_names: Vec = (0 .. variant.fields.len()) + .map(|i| { + let id = aster::id(format!("__field{}", i)); + quote!(ref #id) + }) .collect(); - let pat = builder.pat().enum_() - .id(type_ident).id(variant_ident).build() - .with_pats( - field_names.iter() - .map(|field| builder.pat().ref_id(field)) - ) - .build(); + let pat = quote!(#type_ident::#variant_ident(#(field_names),*)); let block = serialize_tuple_variant( - cx, - builder, type_name, variant_index, variant_name, @@ -381,28 +299,21 @@ fn serialize_variant( &variant.fields, ); - quote_arm!(cx, - $pat => $block - ) + quote! { + #pat => { #block } + } } Style::Struct => { - let mut pat = builder.pat().struct_().id(type_ident).id(variant_ident).build(); - for field in &variant.fields { - let name = match field.ident { - Some(name) => name, - None => cx.span_bug(field.span, "struct variant has unnamed fields"), + let fields = variant.fields.iter().map(|field| { + let id = match field.ident { + Some(ref name) => name.clone(), + None => panic!("struct variant has unnamed fields"), }; - pat = pat.with_field_pat(ast::FieldPat { - ident: name, - pat: builder.pat().ref_id(name), - is_shorthand: true, - }); - } - let pat = pat.build(); + quote!(ref #id) + }); + let pat = quote!(#type_ident::#variant_ident { #(fields),* }); let block = serialize_struct_variant( - cx, - builder, variant_index, variant_name, generics, @@ -411,250 +322,228 @@ fn serialize_variant( item_attrs, ); - quote_arm!(cx, - $pat => $block - ) + quote! { + #pat => { #block } + } } } } fn serialize_newtype_variant( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_name: P, + type_name: String, variant_index: usize, - variant_name: P, - item_ty: P, - generics: &ast::Generics, + variant_name: String, + item_ty: syn::Ty, + generics: &syn::Generics, field: &Field, -) -> P { - let mut field_expr = quote_expr!(cx, __simple_value); +) -> Tokens { + let mut field_expr = quote!(__simple_value); if let Some(path) = field.attrs.serialize_with() { - field_expr = wrap_serialize_with(cx, builder, + field_expr = wrap_serialize_with( &item_ty, generics, &field.ty, path, field_expr); } - quote_block!(cx, { + quote! { _serde::ser::Serializer::serialize_newtype_variant( _serializer, - $type_name, - $variant_index, - $variant_name, - $field_expr, + #type_name, + #variant_index, + #variant_name, + #field_expr, ) - }).unwrap() + } } fn serialize_tuple_variant( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - type_name: P, + type_name: String, variant_index: usize, - variant_name: P, - generics: &ast::Generics, - structure_ty: P, + variant_name: String, + generics: &syn::Generics, + structure_ty: syn::Ty, fields: &[Field], -) -> P { +) -> Tokens { let serialize_stmts = serialize_tuple_struct_visitor( - cx, - builder, structure_ty, fields, generics, true, - cx.ident_of("serialize_tuple_variant_elt"), + aster::id("serialize_tuple_variant_elt"), ); let len = serialize_stmts.len(); - let let_mut = mut_if(cx, len > 0); + let let_mut = mut_if(len > 0); - quote_block!(cx, { - let $let_mut state = try!(_serializer.serialize_tuple_variant($type_name, $variant_index, $variant_name, $len)); - $serialize_stmts + quote! { + let #let_mut state = try!(_serializer.serialize_tuple_variant(#type_name, #variant_index, #variant_name, #len)); + #(serialize_stmts)* _serializer.serialize_tuple_variant_end(state) - }).unwrap() + } } fn serialize_struct_variant( - cx: &ExtCtxt, - builder: &aster::AstBuilder, variant_index: usize, - variant_name: P, - generics: &ast::Generics, - ty: P, + variant_name: String, + generics: &syn::Generics, + ty: syn::Ty, fields: &[Field], item_attrs: &attr::Item, -) -> P { - +) -> Tokens { let serialize_fields = serialize_struct_visitor( - cx, - builder, ty.clone(), fields, &generics, true, - cx.ident_of("serialize_struct_variant_elt"), + aster::id("serialize_struct_variant_elt"), ); - let item_name = name_expr(builder, item_attrs.name()); + let item_name = item_attrs.name().serialize_name(); let mut serialized_fields = fields.iter() .filter(|&field| !field.attrs.skip_serializing()) .peekable(); - let let_mut = mut_if(cx, serialized_fields.peek().is_some()); + let let_mut = mut_if(serialized_fields.peek().is_some()); let len = serialized_fields .map(|field| { - let ident = field.ident.expect("struct has unnamed fields"); - let field_expr = quote_expr!(cx, $ident); + let ident = field.ident.clone().expect("struct has unnamed fields"); match field.attrs.skip_serializing_if() { - Some(path) => quote_expr!(cx, if $path($field_expr) { 0 } else { 1 }), - None => quote_expr!(cx, 1), + Some(path) => quote!(if #path(#ident) { 0 } else { 1 }), + None => quote!(1), } }) - .fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr)); + .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); - quote_block!(cx, { - let $let_mut state = try!(_serializer.serialize_struct_variant( - $item_name, - $variant_index, - $variant_name, - $len, + quote! { + let #let_mut state = try!(_serializer.serialize_struct_variant( + #item_name, + #variant_index, + #variant_name, + #len, )); - $serialize_fields + #(serialize_fields)* _serializer.serialize_struct_variant_end(state) - }).unwrap() + } } fn serialize_tuple_struct_visitor( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - structure_ty: P, + structure_ty: syn::Ty, fields: &[Field], - generics: &ast::Generics, + generics: &syn::Generics, is_enum: bool, - func: ast::Ident, -) -> Vec { + func: syn::Ident, +) -> Vec { fields.iter() .enumerate() .map(|(i, field)| { let mut field_expr = if is_enum { - builder.expr().path().id(format!("__field{}", i)).build() + let id = aster::id(format!("__field{}", i)); + quote!(#id) } else { - builder.expr().ref_().tup_field(i).self_() + quote!(&self.#i) }; let skip = field.attrs.skip_serializing_if() - .map(|path| quote_expr!(cx, $path($field_expr))); + .map(|path| quote!(#path(#field_expr))); if let Some(path) = field.attrs.serialize_with() { - field_expr = wrap_serialize_with(cx, builder, + field_expr = wrap_serialize_with( &structure_ty, generics, &field.ty, path, field_expr); } - let ser = quote_expr!(cx, - try!(_serializer.$func(&mut state, $field_expr)); - ); + let ser = quote! { + try!(_serializer.#func(&mut state, #field_expr)); + }; match skip { - None => quote_stmt!(cx, $ser).unwrap(), - Some(skip) => quote_stmt!(cx, if !$skip { $ser }).unwrap(), + None => ser, + Some(skip) => quote!(if !#skip { #ser }), } }) .collect() } fn serialize_struct_visitor( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - structure_ty: P, + structure_ty: syn::Ty, fields: &[Field], - generics: &ast::Generics, + generics: &syn::Generics, is_enum: bool, - func: ast::Ident, -) -> Vec { + func: syn::Ident, +) -> Vec { fields.iter() .filter(|&field| !field.attrs.skip_serializing()) .map(|field| { - let ident = field.ident.expect("struct has unnamed field"); + let ident = field.ident.clone().expect("struct has unnamed field"); let mut field_expr = if is_enum { - quote_expr!(cx, $ident) + quote!(#ident) } else { - quote_expr!(cx, &self.$ident) + quote!(&self.#ident) }; - let key_expr = name_expr(builder, field.attrs.name()); + let key_expr = field.attrs.name().serialize_name(); let skip = field.attrs.skip_serializing_if() - .map(|path| quote_expr!(cx, $path($field_expr))); + .map(|path| quote!(#path(#field_expr))); if let Some(path) = field.attrs.serialize_with() { - field_expr = wrap_serialize_with(cx, builder, + field_expr = wrap_serialize_with( &structure_ty, generics, &field.ty, path, field_expr) } - let ser = quote_expr!(cx, - try!(_serializer.$func(&mut state, $key_expr, $field_expr)); - ); + let ser = quote! { + try!(_serializer.#func(&mut state, #key_expr, #field_expr)); + }; match skip { - None => quote_stmt!(cx, $ser).unwrap(), - Some(skip) => quote_stmt!(cx, if !$skip { $ser }).unwrap(), + None => ser, + Some(skip) => quote!(if !#skip { #ser }), } }) .collect() } fn wrap_serialize_with( - cx: &ExtCtxt, - builder: &aster::AstBuilder, - item_ty: &P, - generics: &ast::Generics, - field_ty: &P, - path: &ast::Path, - value: P, -) -> P { + item_ty: &syn::Ty, + generics: &syn::Generics, + field_ty: &syn::Ty, + path: &syn::Path, + value: Tokens, +) -> Tokens { let where_clause = &generics.where_clause; - let wrapper_generics = builder.from_generics(generics.clone()) + let wrapper_generics = aster::from_generics(generics.clone()) .add_lifetime_bound("'__a") .lifetime_name("'__a") .build(); - let wrapper_ty = builder.path() + let wrapper_ty = aster::path() .segment("__SerializeWith") .with_generics(wrapper_generics.clone()) .build() .build(); - quote_expr!(cx, { - struct __SerializeWith $wrapper_generics $where_clause { - value: &'__a $field_ty, - phantom: ::std::marker::PhantomData<$item_ty>, - } + quote! { + { + struct __SerializeWith #wrapper_generics #where_clause { + value: &'__a #field_ty, + phantom: ::std::marker::PhantomData<#item_ty>, + } - impl $wrapper_generics _serde::ser::Serialize for $wrapper_ty $where_clause { - fn serialize<__S>(&self, __s: &mut __S) -> ::std::result::Result<(), __S::Error> - where __S: _serde::ser::Serializer - { - $path(self.value, __s) + impl #wrapper_generics _serde::ser::Serialize for #wrapper_ty #where_clause { + fn serialize<__S>(&self, __s: &mut __S) -> ::std::result::Result<(), __S::Error> + where __S: _serde::ser::Serializer + { + #path(self.value, __s) + } + } + + __SerializeWith { + value: #value, + phantom: ::std::marker::PhantomData::<#item_ty>, } } - - __SerializeWith { - value: $value, - phantom: ::std::marker::PhantomData::<$item_ty>, - } - }) -} - -fn name_expr( - builder: &aster::AstBuilder, - name: &attr::Name, -) -> P { - builder.expr().str(name.serialize_name()) + } } // Serialization of an empty struct results in code like: @@ -663,10 +552,10 @@ fn name_expr( // serializer.serialize_struct_end(state) // // where we want to omit the `mut` to avoid a warning. -fn mut_if(cx: &ExtCtxt, is_mut: bool) -> Vec { +fn mut_if(is_mut: bool) -> Option { if is_mut { - quote_tokens!(cx, mut) + Some(quote!(mut)) } else { - Vec::new() + None } } diff --git a/serde_codegen/src/span.rs b/serde_codegen/src/span.rs deleted file mode 100644 index 5421868d..00000000 --- a/serde_codegen/src/span.rs +++ /dev/null @@ -1,43 +0,0 @@ -use syntax::ast; -use syntax::codemap::{self, ExpnId, Span}; -use syntax::ext::base::{Annotatable, ExtCtxt}; -use syntax::fold::{self, Folder}; -use syntax::parse::token::intern; -use syntax::ptr::P; - -pub fn record_expansion( - cx: &ExtCtxt, - item: P, - derive: &str, -) -> Annotatable { - let info = codemap::ExpnInfo { - call_site: codemap::DUMMY_SP, - callee: codemap::NameAndSpan { - format: codemap::MacroAttribute(intern(&format!("derive({})", derive))), - span: None, - allow_internal_unstable: false, - }, - }; - let expn_id = cx.codemap().record_expansion(info); - - let mut respanner = Respanner { expn_id: expn_id }; - let item = item.map(|item| respanner.fold_item_simple(item)); - Annotatable::Item(item) -} - -struct Respanner { - expn_id: ExpnId, -} - -impl Folder for Respanner { - fn new_span(&mut self, span: Span) -> Span { - Span { - expn_id: self.expn_id, - .. span - } - } - - fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { - fold::noop_fold_mac(mac, self) - } -} diff --git a/serde_codegen_internals/Cargo.toml b/serde_codegen_internals/Cargo.toml index 3219579d..2a621f1f 100644 --- a/serde_codegen_internals/Cargo.toml +++ b/serde_codegen_internals/Cargo.toml @@ -11,11 +11,8 @@ keywords = ["serde", "serialization"] include = ["Cargo.toml", "src/**/*.rs"] [features] -default = ["with-syntex"] unstable-testing = ["clippy"] -with-syntex = ["syntex_syntax", "syntex_errors"] [dependencies] clippy = { version = "^0.*", optional = true } -syntex_syntax = { version = "^0.43.0", optional = true } -syntex_errors = { version = "^0.43.0", optional = true } +syn = "0.5" diff --git a/serde_codegen_internals/src/ast.rs b/serde_codegen_internals/src/ast.rs index dd79944f..d62ad807 100644 --- a/serde_codegen_internals/src/ast.rs +++ b/serde_codegen_internals/src/ast.rs @@ -1,17 +1,12 @@ -use syntax::ast; -use syntax::codemap; -use syntax::ext::base::ExtCtxt; -use syntax::ptr::P; - +use syn; use attr; -use Error; +use Ctxt; pub struct Item<'a> { - pub ident: ast::Ident, - pub span: codemap::Span, + pub ident: syn::Ident, pub attrs: attr::Item, pub body: Body<'a>, - pub generics: &'a ast::Generics, + pub generics: &'a syn::Generics, } pub enum Body<'a> { @@ -20,18 +15,16 @@ pub enum Body<'a> { } pub struct Variant<'a> { - pub ident: ast::Ident, - pub span: codemap::Span, + pub ident: syn::Ident, pub attrs: attr::Variant, pub style: Style, pub fields: Vec>, } pub struct Field<'a> { - pub ident: Option, - pub span: codemap::Span, + pub ident: Option, pub attrs: attr::Field, - pub ty: &'a P, + pub ty: &'a syn::Ty, } pub enum Style { @@ -42,33 +35,25 @@ pub enum Style { } impl<'a> Item<'a> { - pub fn from_ast( - cx: &ExtCtxt, - item: &'a ast::Item, - ) -> Result, Error> { + pub fn from_ast(cx: &Ctxt, item: &'a syn::Item) -> Item<'a> { let attrs = attr::Item::from_ast(cx, item); - let (body, generics) = match item.node { - ast::ItemKind::Enum(ref enum_def, ref generics) => { - let variants = enum_from_ast(cx, enum_def); - (Body::Enum(variants), generics) + let body = match item.body { + syn::Body::Enum(ref variants) => { + Body::Enum(enum_from_ast(cx, variants)) } - ast::ItemKind::Struct(ref variant_data, ref generics) => { + syn::Body::Struct(ref variant_data) => { let (style, fields) = struct_from_ast(cx, variant_data); - (Body::Struct(style, fields), generics) - } - _ => { - return Err(Error::UnexpectedItemKind); + Body::Struct(style, fields) } }; - Ok(Item { - ident: item.ident, - span: item.span, + Item { + ident: item.ident.clone(), attrs: attrs, body: body, - generics: generics, - }) + generics: &item.generics, + } } } @@ -86,16 +71,12 @@ impl<'a> Body<'a> { } } -fn enum_from_ast<'a>( - cx: &ExtCtxt, - enum_def: &'a ast::EnumDef, -) -> Vec> { - enum_def.variants.iter() +fn enum_from_ast<'a>(cx: &Ctxt, variants: &'a [syn::Variant]) -> Vec> { + variants.iter() .map(|variant| { - let (style, fields) = struct_from_ast(cx, &variant.node.data); + let (style, fields) = struct_from_ast(cx, &variant.data); Variant { - ident: variant.node.name, - span: variant.span, + ident: variant.ident.clone(), attrs: attr::Variant::from_ast(cx, variant), style: style, fields: fields, @@ -104,36 +85,29 @@ fn enum_from_ast<'a>( .collect() } -fn struct_from_ast<'a>( - cx: &ExtCtxt, - variant_data: &'a ast::VariantData, -) -> (Style, Vec>) { - match *variant_data { - ast::VariantData::Struct(ref fields, _) => { +fn struct_from_ast<'a>(cx: &Ctxt, data: &'a syn::VariantData) -> (Style, Vec>) { + match *data { + syn::VariantData::Struct(ref fields) => { (Style::Struct, fields_from_ast(cx, fields)) } - ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { + syn::VariantData::Tuple(ref fields) if fields.len() == 1 => { (Style::Newtype, fields_from_ast(cx, fields)) } - ast::VariantData::Tuple(ref fields, _) => { + syn::VariantData::Tuple(ref fields) => { (Style::Tuple, fields_from_ast(cx, fields)) } - ast::VariantData::Unit(_) => { + syn::VariantData::Unit => { (Style::Unit, Vec::new()) } } } -fn fields_from_ast<'a>( - cx: &ExtCtxt, - fields: &'a [ast::StructField], -) -> Vec> { +fn fields_from_ast<'a>(cx: &Ctxt, fields: &'a [syn::Field]) -> Vec> { fields.iter() .enumerate() .map(|(i, field)| { Field { - ident: field.ident, - span: field.span, + ident: field.ident.clone(), attrs: attr::Field::from_ast(cx, i, field), ty: &field.ty, } diff --git a/serde_codegen_internals/src/attr.rs b/serde_codegen_internals/src/attr.rs index a4dd2ce3..02b72b6c 100644 --- a/serde_codegen_internals/src/attr.rs +++ b/serde_codegen_internals/src/attr.rs @@ -1,15 +1,5 @@ -use std::rc::Rc; - -use syntax::ast; -use syntax::attr::{self, HasAttrs}; -use syntax::codemap::{Span, Spanned, respan}; -use syntax::ext::base::ExtCtxt; -use syntax::fold::Folder; -use syntax::parse::parser::{Parser, PathStyle}; -use syntax::parse::token::{self, InternedString}; -use syntax::parse; -use syntax::print::pprust::{lit_to_string, meta_item_to_string, meta_list_item_to_string}; -use syntax::tokenstream::{self, TokenTree}; +use Ctxt; +use syn::{self, Ident}; // This module handles parsing of `#[serde(...)]` attributes. The entrypoints // are `attr::Item::from_ast`, `attr::Variant::from_ast`, and @@ -19,13 +9,14 @@ use syntax::tokenstream::{self, TokenTree}; // user will see errors simultaneously for all bad attributes in the crate // rather than just the first. -struct Attr<'a, 'b: 'a, T> { - cx: &'a ExtCtxt<'b>, +struct Attr<'c, T> { + cx: &'c Ctxt, name: &'static str, - value: Option>, + value: Option, } -impl<'a, 'b, T> Attr<'a, 'b, T> { - fn none(cx: &'a ExtCtxt<'b>, name: &'static str) -> Self { + +impl<'c, T> Attr<'c, T> { + fn none(cx: &'c Ctxt, name: &'static str) -> Self { Attr { cx: cx, name: name, @@ -33,47 +24,40 @@ impl<'a, 'b, T> Attr<'a, 'b, T> { } } - fn set(&mut self, span: Span, t: T) { - if let Some(Spanned { span: prev_span, .. }) = self.value { - let mut err = self.cx.struct_span_err( - span, - &format!("duplicate serde attribute `{}`", self.name)); - err.span_help(prev_span, "previously set here"); - err.emit(); + fn set(&mut self, value: T) { + if self.value.is_some() { + self.cx.error(format!("duplicate serde attribute `{}`", self.name)); } else { - self.value = Some(respan(span, t)); + self.value = Some(value); } } - fn set_opt(&mut self, v: Option>) { - if let Some(v) = v { - self.set(v.span, v.node); + fn set_opt(&mut self, value: Option) { + if let Some(value) = value { + self.set(value); } } - fn set_if_none(&mut self, span: Span, t: T) { + fn set_if_none(&mut self, value: T) { if self.value.is_none() { - self.value = Some(respan(span, t)); + self.value = Some(value); } } fn get(self) -> Option { - self.value.map(|spanned| spanned.node) - } - - fn get_spanned(self) -> Option> { self.value } } -struct BoolAttr<'a, 'b: 'a>(Attr<'a, 'b, ()>); -impl<'a, 'b> BoolAttr<'a, 'b> { - fn none(cx: &'a ExtCtxt<'b>, name: &'static str) -> Self { +struct BoolAttr<'c>(Attr<'c, ()>); + +impl<'c> BoolAttr<'c> { + fn none(cx: &'c Ctxt, name: &'static str) -> Self { BoolAttr(Attr::none(cx, name)) } - fn set_true(&mut self, span: Span) { - self.0.set(span, ()); + fn set_true(&mut self) { + self.0.set(()); } fn get(&self) -> bool { @@ -83,18 +67,18 @@ impl<'a, 'b> BoolAttr<'a, 'b> { #[derive(Debug)] pub struct Name { - serialize: InternedString, - deserialize: InternedString, + serialize: String, + deserialize: String, } impl Name { /// Return the container name for the container when serializing. - pub fn serialize_name(&self) -> InternedString { + pub fn serialize_name(&self) -> String { self.serialize.clone() } /// Return the container name for the container when deserializing. - pub fn deserialize_name(&self) -> InternedString { + pub fn deserialize_name(&self) -> String { self.deserialize.clone() } } @@ -104,35 +88,30 @@ impl Name { pub struct Item { name: Name, deny_unknown_fields: bool, - ser_bound: Option>, - de_bound: Option>, + ser_bound: Option>, + de_bound: Option>, } impl Item { /// Extract out the `#[serde(...)]` attributes from an item. - pub fn from_ast(cx: &ExtCtxt, item: &ast::Item) -> Self { + pub fn from_ast(cx: &Ctxt, item: &syn::Item) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields"); let mut ser_bound = Attr::none(cx, "bound"); let mut de_bound = Attr::none(cx, "bound"); - let ident = item.ident.name.as_str(); - - 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) { for meta_item in meta_items { - let span = meta_item.span; - match meta_item.node { + match meta_item { // Parse `#[serde(rename="foo")]` - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => { - if let Ok(s) = get_str_from_lit(cx, name, lit) { - ser_name.set(span, s.clone()); - de_name.set(span, s); - } + syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { + ser_name.set(lit.clone()); + de_name.set(lit.clone()); } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` - ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => { + syn::MetaItem::List(ref name, ref meta_items) if name == "rename" => { if let Ok((ser, de)) = get_renames(cx, meta_items) { ser_name.set_opt(ser); de_name.set_opt(de); @@ -140,20 +119,20 @@ impl Item { } // Parse `#[serde(deny_unknown_fields)]` - ast::MetaItemKind::Word(ref name) if name == &"deny_unknown_fields" => { - deny_unknown_fields.set_true(span); + syn::MetaItem::Word(ref name) if name == "deny_unknown_fields" => { + deny_unknown_fields.set_true(); } // Parse `#[serde(bound="D: Serialize")]` - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => { + syn::MetaItem::NameValue(ref name, ref lit) if name == "bound" => { if let Ok(where_predicates) = parse_lit_into_where(cx, name, lit) { - ser_bound.set(span, where_predicates.clone()); - de_bound.set(span, where_predicates); + ser_bound.set(where_predicates.clone()); + de_bound.set(where_predicates); } } // Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]` - ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => { + syn::MetaItem::List(ref name, ref meta_items) if name == "bound" => { if let Ok((ser, de)) = get_where_predicates(cx, meta_items) { ser_bound.set_opt(ser); de_bound.set_opt(de); @@ -161,10 +140,8 @@ impl Item { } _ => { - cx.span_err( - meta_item.span, - &format!("unknown serde container attribute `{}`", - meta_item_to_string(&meta_item))); + // TODO include name of attr + cx.error("unknown serde container attribute"); } } } @@ -172,8 +149,8 @@ impl Item { Item { name: Name { - serialize: ser_name.get().unwrap_or(ident.clone()), - deserialize: de_name.get().unwrap_or(ident), + serialize: ser_name.get().unwrap_or_else(|| item.ident.to_string()), + deserialize: de_name.get().unwrap_or_else(|| item.ident.to_string()), }, deny_unknown_fields: deny_unknown_fields.get(), ser_bound: ser_bound.get(), @@ -189,11 +166,11 @@ impl Item { self.deny_unknown_fields } - pub fn ser_bound(&self) -> Option<&[ast::WherePredicate]> { + pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> { self.ser_bound.as_ref().map(|vec| &vec[..]) } - pub fn de_bound(&self) -> Option<&[ast::WherePredicate]> { + pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> { self.de_bound.as_ref().map(|vec| &vec[..]) } } @@ -205,26 +182,21 @@ pub struct Variant { } impl Variant { - pub fn from_ast(cx: &ExtCtxt, variant: &ast::Variant) -> Self { + pub fn from_ast(cx: &Ctxt, variant: &syn::Variant) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); - let ident = variant.node.name.name.as_str(); - - for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) { + for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) { for meta_item in meta_items { - let span = meta_item.span; - match meta_item.node { + match meta_item { // Parse `#[serde(rename="foo")]` - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => { - if let Ok(s) = get_str_from_lit(cx, name, lit) { - ser_name.set(span, s.clone()); - de_name.set(span, s); - } + syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { + ser_name.set(lit.clone()); + de_name.set(lit.clone()); } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` - ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => { + syn::MetaItem::List(ref name, ref meta_items) if name == "rename" => { if let Ok((ser, de)) = get_renames(cx, meta_items) { ser_name.set_opt(ser); de_name.set_opt(de); @@ -232,10 +204,8 @@ impl Variant { } _ => { - cx.span_err( - meta_item.span, - &format!("unknown serde variant attribute `{}`", - meta_item_to_string(&meta_item))); + // TODO include attribute + cx.error("unknown serde variant attribute"); } } } @@ -243,8 +213,8 @@ impl Variant { Variant { name: Name { - serialize: ser_name.get().unwrap_or(ident.clone()), - deserialize: de_name.get().unwrap_or(ident), + serialize: ser_name.get().unwrap_or_else(|| variant.ident.to_string()), + deserialize: de_name.get().unwrap_or_else(|| variant.ident.to_string()), }, } } @@ -260,12 +230,12 @@ pub struct Field { name: Name, skip_serializing: bool, skip_deserializing: bool, - skip_serializing_if: Option, + skip_serializing_if: Option, default: FieldDefault, - serialize_with: Option, - deserialize_with: Option, - ser_bound: Option>, - de_bound: Option>, + serialize_with: Option, + deserialize_with: Option, + ser_bound: Option>, + de_bound: Option>, } /// Represents the default to use for a field when deserializing. @@ -276,14 +246,14 @@ pub enum FieldDefault { /// The default is given by `std::default::Default::default()`. Default, /// The default is given by this function. - Path(ast::Path), + Path(syn::Path), } impl Field { /// Extract out the `#[serde(...)]` attributes from a struct field. - pub fn from_ast(cx: &ExtCtxt, + pub fn from_ast(cx: &Ctxt, index: usize, - field: &ast::StructField) -> Self { + field: &syn::Field) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); let mut skip_serializing = BoolAttr::none(cx, "skip_serializing"); @@ -296,24 +266,21 @@ impl Field { let mut de_bound = Attr::none(cx, "bound"); let ident = match field.ident { - Some(ident) => ident.name.as_str(), - None => token::intern_and_get_ident(&index.to_string()), + Some(ref ident) => ident.to_string(), + None => index.to_string(), }; for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) { for meta_item in meta_items { - let span = meta_item.span; - match meta_item.node { + match meta_item { // Parse `#[serde(rename="foo")]` - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => { - if let Ok(s) = get_str_from_lit(cx, name, lit) { - ser_name.set(span, s.clone()); - de_name.set(span, s); - } + syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { + ser_name.set(lit.clone()); + de_name.set(lit.clone()); } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` - ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => { + syn::MetaItem::List(ref name, ref meta_items) if name == "rename" => { if let Ok((ser, de)) = get_renames(cx, meta_items) { ser_name.set_opt(ser); de_name.set_opt(de); @@ -321,58 +288,58 @@ impl Field { } // Parse `#[serde(default)]` - ast::MetaItemKind::Word(ref name) if name == &"default" => { - default.set(span, FieldDefault::Default); + syn::MetaItem::Word(ref name) if name == "default" => { + default.set(FieldDefault::Default); } // Parse `#[serde(default="...")]` - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => { + syn::MetaItem::NameValue(ref name, ref lit) if name == "default" => { if let Ok(path) = parse_lit_into_path(cx, name, lit) { - default.set(span, FieldDefault::Path(path)); + default.set(FieldDefault::Path(path)); } } // Parse `#[serde(skip_serializing)]` - ast::MetaItemKind::Word(ref name) if name == &"skip_serializing" => { - skip_serializing.set_true(span); + syn::MetaItem::Word(ref name) if name == "skip_serializing" => { + skip_serializing.set_true(); } // Parse `#[serde(skip_deserializing)]` - ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => { - skip_deserializing.set_true(span); + syn::MetaItem::Word(ref name) if name == "skip_deserializing" => { + skip_deserializing.set_true(); } // Parse `#[serde(skip_serializing_if="...")]` - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => { + syn::MetaItem::NameValue(ref name, ref lit) if name == "skip_serializing_if" => { if let Ok(path) = parse_lit_into_path(cx, name, lit) { - skip_serializing_if.set(span, path); + skip_serializing_if.set(path); } } // Parse `#[serde(serialize_with="...")]` - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => { + syn::MetaItem::NameValue(ref name, ref lit) if name == "serialize_with" => { if let Ok(path) = parse_lit_into_path(cx, name, lit) { - serialize_with.set(span, path); + serialize_with.set(path); } } // Parse `#[serde(deserialize_with="...")]` - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => { + syn::MetaItem::NameValue(ref name, ref lit) if name == "deserialize_with" => { if let Ok(path) = parse_lit_into_path(cx, name, lit) { - deserialize_with.set(span, path); + deserialize_with.set(path); } } // Parse `#[serde(bound="D: Serialize")]` - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => { + syn::MetaItem::NameValue(ref name, ref lit) if name == "bound" => { if let Ok(where_predicates) = parse_lit_into_where(cx, name, lit) { - ser_bound.set(span, where_predicates.clone()); - de_bound.set(span, where_predicates); + ser_bound.set(where_predicates.clone()); + de_bound.set(where_predicates); } } // Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]` - ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => { + syn::MetaItem::List(ref name, ref meta_items) if name == "bound" => { if let Ok((ser, de)) = get_where_predicates(cx, meta_items) { ser_bound.set_opt(ser); de_bound.set_opt(de); @@ -380,10 +347,8 @@ impl Field { } _ => { - cx.span_err( - meta_item.span, - &format!("unknown serde field attribute `{}`", - meta_item_to_string(&meta_item))); + // TODO include attribute + cx.error("unknown serde field attribute"); } } } @@ -391,8 +356,8 @@ impl Field { // Is skip_deserializing, initialize the field to Default::default() // unless a different default is specified by `#[serde(default="...")]` - if let Some(Spanned { span, .. }) = skip_deserializing.0.value { - default.set_if_none(span, FieldDefault::Default); + if skip_deserializing.0.value.is_some() { + default.set_if_none(FieldDefault::Default); } Field { @@ -423,7 +388,7 @@ impl Field { self.skip_deserializing } - pub fn skip_serializing_if(&self) -> Option<&ast::Path> { + pub fn skip_serializing_if(&self) -> Option<&syn::Path> { self.skip_serializing_if.as_ref() } @@ -431,230 +396,95 @@ impl Field { &self.default } - pub fn serialize_with(&self) -> Option<&ast::Path> { + pub fn serialize_with(&self) -> Option<&syn::Path> { self.serialize_with.as_ref() } - pub fn deserialize_with(&self) -> Option<&ast::Path> { + pub fn deserialize_with(&self) -> Option<&syn::Path> { self.deserialize_with.as_ref() } - pub fn ser_bound(&self) -> Option<&[ast::WherePredicate]> { + pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> { self.ser_bound.as_ref().map(|vec| &vec[..]) } - pub fn de_bound(&self) -> Option<&[ast::WherePredicate]> { + pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> { self.de_bound.as_ref().map(|vec| &vec[..]) } } -type SerAndDe = (Option>, Option>); +type SerAndDe = (Option, Option); fn get_ser_and_de( - cx: &ExtCtxt, + cx: &Ctxt, attribute: &'static str, - items: &[ast::NestedMetaItem], + items: &[syn::MetaItem], f: F ) -> Result, ()> - where F: Fn(&ExtCtxt, &str, &ast::Lit) -> Result, + where F: Fn(&Ctxt, &Ident, &str) -> Result, { let mut ser_item = Attr::none(cx, attribute); let mut de_item = Attr::none(cx, attribute); for item in items { - match item.node { - ast::NestedMetaItemKind::MetaItem(ref meta_item) => { - match meta_item.node { - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => { - if let Ok(v) = f(cx, name, lit) { - ser_item.set(item.span, v); - } - } + match *item { + syn::MetaItem::NameValue(ref name, ref lit) if name == "serialize" => { + if let Ok(v) = f(cx, name, lit) { + ser_item.set(v); + } + } - ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => { - if let Ok(v) = f(cx, name, lit) { - de_item.set(item.span, v); - } - } - - _ => { - cx.span_err( - item.span, - &format!("unknown {} attribute `{}`", - attribute, - meta_item_to_string(meta_item))); - - return Err(()); - } + syn::MetaItem::NameValue(ref name, ref lit) if name == "deserialize" => { + if let Ok(v) = f(cx, name, lit) { + de_item.set(v); } } _ => { - cx.span_err( - item.span, - &format!("unknown {} attribute `{}`", - attribute, - meta_list_item_to_string(item))); - + cx.error(format!("bad {} attribute", attribute)); return Err(()); } } } - Ok((ser_item.get_spanned(), de_item.get_spanned())) + Ok((ser_item.get(), de_item.get())) } fn get_renames( - cx: &ExtCtxt, - items: &[ast::NestedMetaItem], -) -> Result, ()> { - get_ser_and_de(cx, "rename", items, get_str_from_lit) + cx: &Ctxt, + items: &[syn::MetaItem], +) -> Result, ()> { + get_ser_and_de(cx, "rename", items, |_, _, s| Ok(s.to_owned())) } fn get_where_predicates( - cx: &ExtCtxt, - items: &[ast::NestedMetaItem], -) -> Result>, ()> { + cx: &Ctxt, + items: &[syn::MetaItem], +) -> Result>, ()> { get_ser_and_de(cx, "bound", items, parse_lit_into_where) } -pub fn get_serde_meta_items(attr: &ast::Attribute) -> Option> { - match attr.node.value.node { - ast::MetaItemKind::List(ref name, ref items) if name == &"serde" => { - attr::mark_used(attr); - Some(items.iter().filter_map(|item| { - match item.node { - ast::NestedMetaItemKind::MetaItem(ref meta_item) => { - Some((*meta_item.clone()).clone()) - } - _ => None, - } - }).collect()) +pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option> { + match attr.value { + syn::MetaItem::List(ref name, ref items) if name == "serde" => { + Some(items.iter().cloned().collect()) } _ => None } } -/// This syntax folder rewrites tokens to say their spans are coming from a macro context. -struct Respanner<'a, 'b: 'a> { - cx: &'a ExtCtxt<'b>, +fn parse_lit_into_path(cx: &Ctxt, name: &Ident, lit: &str) -> Result { + // TODO handle error + Ok(syn::parse_path(lit).unwrap()) } -impl<'a, 'b> Folder for Respanner<'a, 'b> { - fn fold_tt(&mut self, tt: &TokenTree) -> TokenTree { - match *tt { - TokenTree::Token(span, ref tok) => { - TokenTree::Token( - self.new_span(span), - self.fold_token(tok.clone()) - ) - } - TokenTree::Delimited(span, ref delimed) => { - TokenTree::Delimited( - self.new_span(span), - Rc::new(tokenstream::Delimited { - delim: delimed.delim, - open_span: delimed.open_span, - tts: self.fold_tts(&delimed.tts), - close_span: delimed.close_span, - }) - ) - } - TokenTree::Sequence(span, ref seq) => { - TokenTree::Sequence( - self.new_span(span), - Rc::new(tokenstream::SequenceRepetition { - tts: self.fold_tts(&seq.tts), - separator: seq.separator.clone().map(|tok| self.fold_token(tok)), - ..**seq - }) - ) - } - } - } - - fn new_span(&mut self, span: Span) -> Span { - Span { - lo: span.lo, - hi: span.hi, - expn_id: self.cx.backtrace(), - } - } -} - -fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result { - match lit.node { - ast::LitKind::Str(ref s, _) => Ok(s.clone()), - _ => { - cx.span_err( - lit.span, - &format!("serde annotation `{}` must be a string, not `{}`", - name, - lit_to_string(lit))); - - Err(()) - } - } -} - -// If we just parse a string literal from an attibute, any syntax errors in the -// 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 -// into a token tree. Then we'll update those spans to say they're coming from a -// macro context that originally came from the attribnute, and then finally -// parse them into an expression or where-clause. -fn parse_string_via_tts(cx: &ExtCtxt, name: &str, string: String, action: F) -> Result - where F: for<'a> Fn(&'a mut Parser) -> parse::PResult<'a, T>, -{ - let tts = panictry!(parse::parse_tts_from_source_str( - format!("", name), - string, - cx.cfg(), - cx.parse_sess())); - - // Respan the spans to say they are all coming from this macro. - let tts = Respanner { cx: cx }.fold_tts(&tts); - - let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts); - - let path = match action(&mut parser) { - Ok(path) => path, - Err(mut e) => { - e.emit(); - return Err(()); - } - }; - - // Make sure to error out if there are trailing characters in the stream. - match parser.expect(&token::Eof) { - Ok(()) => { } - Err(mut e) => { - e.emit(); - return Err(()); - } - } - - Ok(path) -} - -fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result { - 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, ()> { - let string = try!(get_str_from_lit(cx, name, lit)); - if string.is_empty() { +fn parse_lit_into_where(cx: &Ctxt, name: &Ident, lit: &str) -> Result, ()> { + if lit.is_empty() { return Ok(Vec::new()); } - let where_string = format!("where {}", string); + let where_string = format!("where {}", lit); - parse_string_via_tts(cx, name, where_string, |parser| { - let where_clause = try!(parser.parse_where_clause()); - Ok(where_clause.predicates) - }) + // TODO handle error + Ok(syn::parse_where_clause(&where_string).unwrap().predicates) } diff --git a/serde_codegen_internals/src/ctxt.rs b/serde_codegen_internals/src/ctxt.rs new file mode 100644 index 00000000..a516aec8 --- /dev/null +++ b/serde_codegen_internals/src/ctxt.rs @@ -0,0 +1,29 @@ +use std::fmt::Display; +use std::cell::Cell; + +#[derive(Default)] +pub struct Ctxt { + err_count: Cell, +} + +impl Ctxt { + pub fn new() -> Self { + Default::default() + } + + pub fn error(&self, msg: T) { + println!("{}", msg); + self.err_count.set(self.err_count.get() + 1); + } +} + +impl Drop for Ctxt { + fn drop(&mut self) { + let err_count = self.err_count.get(); + if err_count == 1 { + panic!("1 error"); + } else if err_count > 1 { + panic!("{} errors", err_count); + } + } +} diff --git a/serde_codegen_internals/src/error.rs b/serde_codegen_internals/src/error.rs deleted file mode 100644 index 08337c0b..00000000 --- a/serde_codegen_internals/src/error.rs +++ /dev/null @@ -1,19 +0,0 @@ -use std::error; -use std::fmt; - -#[derive(Debug)] -pub enum Error { - UnexpectedItemKind, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "expected a struct or enum") - } -} - -impl error::Error for Error { - fn description(&self) -> &str { - "expected a struct or enum" - } -} diff --git a/serde_codegen_internals/src/lib.rs b/serde_codegen_internals/src/lib.rs index 89f20aa6..0f37f5fa 100644 --- a/serde_codegen_internals/src/lib.rs +++ b/serde_codegen_internals/src/lib.rs @@ -1,21 +1,10 @@ #![cfg_attr(feature = "clippy", plugin(clippy))] #![cfg_attr(feature = "clippy", feature(plugin))] -#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))] -#[cfg(feature = "with-syntex")] -#[macro_use] -extern crate syntex_syntax as syntax; -#[cfg(feature = "with-syntex")] -extern crate syntex_errors as errors; - -#[cfg(not(feature = "with-syntex"))] -#[macro_use] -extern crate syntax; -#[cfg(not(feature = "with-syntex"))] -extern crate rustc_errors as errors; +extern crate syn; pub mod ast; pub mod attr; -mod error; -pub use error::Error; +mod ctxt; +pub use ctxt::Ctxt; diff --git a/serde_derive/Cargo.toml b/serde_derive/Cargo.toml index 66930b14..da38783d 100644 --- a/serde_derive/Cargo.toml +++ b/serde_derive/Cargo.toml @@ -14,8 +14,11 @@ include = ["Cargo.toml", "src/**/*.rs"] name = "serde_derive" rustc-macro = true -[dependencies] -serde_codegen = { version = "=0.8.8", path = "../serde_codegen" } +[dependencies.serde_codegen] +version = "=0.8.8" +path = "../serde_codegen" +default-features = false +features = ["with-syn"] [dev-dependencies] fnv = "1.0" diff --git a/serde_derive/src/lib.rs b/serde_derive/src/lib.rs index aa7bba60..5c80708a 100644 --- a/serde_derive/src/lib.rs +++ b/serde_derive/src/lib.rs @@ -9,13 +9,13 @@ use rustc_macro::TokenStream; #[rustc_macro_derive(Serialize)] pub fn derive_serialize(input: TokenStream) -> TokenStream { let item = format!("#[derive(Serialize)]\n{}", input); - let expanded = serde_codegen::expand_str(&item).unwrap(); + let expanded = serde_codegen::expand_single_item(&item); expanded.parse().unwrap() } #[rustc_macro_derive(Deserialize)] pub fn derive_deserialize(input: TokenStream) -> TokenStream { let item = format!("#[derive(Deserialize)]\n{}", input); - let expanded = serde_codegen::expand_str(&item).unwrap(); + let expanded = serde_codegen::expand_single_item(&item); expanded.parse().unwrap() } From 55e5f194370d596ccafc1e12a917a9294b818c17 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 12 Sep 2016 00:04:21 -0700 Subject: [PATCH 02/15] Same for deserialization --- serde_codegen/src/de.rs | 987 +++++++++++++++++---------------------- serde_codegen/src/lib.rs | 82 ++-- 2 files changed, 475 insertions(+), 594 deletions(-) diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index b630ede0..dae4ca65 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}; +use internals::{self, attr}; -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; - } +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; - } - }; + let impl_generics = build_impl_generics(&item); - if check_no_str(cx, &item).is_err() { - return; - } - - let builder = aster::AstBuilder::new().span(span); - - 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 7de4db6d..d3f0cab1 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}; +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; + } + }; -#[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; + use syntax::print::pprust; + let s = pprust::item_to_string(item); + + let syn_item = syn::parse_item(&s).unwrap(); + let expanded = $pkg::$func(&syn_item).to_string(); + + 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())); } }; - - 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(); - - 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())); } +#[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:: }; From a69b82c7c594569a8c0eba30b3749aed905b415a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 12 Sep 2016 01:26:47 -0700 Subject: [PATCH 03/15] Recursion limit --- serde_codegen/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index d3f0cab1..8dbfdf31 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -1,11 +1,12 @@ -#![recursion_limit = "1000"] - #![cfg_attr(feature = "clippy", plugin(clippy))] #![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "clippy", allow(too_many_arguments))] #![cfg_attr(feature = "clippy", allow(used_underscore_binding))] #![cfg_attr(feature = "with-libsyntax", feature(rustc_private, plugin))] +// The `quote!` macro requires deep recursion. +#![recursion_limit = "192"] + extern crate serde_codegen_internals as internals; #[cfg(feature = "with-syntex")] From 1eca7766ba67ed9234d099fbd53f12bf19222deb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 24 Sep 2016 09:54:10 -0700 Subject: [PATCH 04/15] Bump syn dependency --- serde_codegen/Cargo.toml | 2 +- serde_codegen/src/de.rs | 2 +- serde_codegen/src/lib.rs | 13 ++++---- serde_codegen/src/ser.rs | 2 +- serde_codegen_internals/Cargo.toml | 2 +- serde_codegen_internals/src/ast.rs | 2 +- serde_codegen_internals/src/attr.rs | 47 ++++++++++++++++++++--------- 7 files changed, 44 insertions(+), 26 deletions(-) diff --git a/serde_codegen/Cargo.toml b/serde_codegen/Cargo.toml index 9b469f1c..132547be 100644 --- a/serde_codegen/Cargo.toml +++ b/serde_codegen/Cargo.toml @@ -25,6 +25,6 @@ with-syn = [] clippy = { version = "^0.*", optional = true } quote = "0.1" serde_codegen_internals = { version = "=0.8.9", default-features = false, path = "../serde_codegen_internals" } -syn = { version = "0.5", features = ["aster", "visit"] } +syn = { version = "0.7", features = ["aster", "visit"] } syntex = { version = "^0.44.0", optional = true } syntex_syntax = { version = "^0.44.0", optional = true } diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index dae4ca65..d8a147d0 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -5,7 +5,7 @@ use bound; use internals::ast::{Body, Field, Item, Style, Variant}; use internals::{self, attr}; -pub fn expand_derive_deserialize(item: &syn::Item) -> Tokens { +pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Tokens { let item = { let ctxt = internals::Ctxt::new(); let item = Item::from_ast(&ctxt, item); diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index 8dbfdf31..fed63700 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -143,7 +143,7 @@ macro_rules! shim { use syntax::print::pprust; let s = pprust::item_to_string(item); - let syn_item = syn::parse_item(&s).unwrap(); + let syn_item = syn::parse_macro_input(&s).unwrap(); let expanded = $pkg::$func(&syn_item).to_string(); use syntax::parse; @@ -163,7 +163,7 @@ 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(); + let syn_item = syn::parse_macro_input(item).unwrap(); let (ser, de, syn_item) = strip_serde_derives(syn_item); let expanded_ser = if ser { Some(ser::expand_derive_serialize(&syn_item)) @@ -178,10 +178,10 @@ pub fn expand_single_item(item: &str) -> String { let syn_item = strip_serde_attrs(syn_item); return quote!(#expanded_ser #expanded_de #syn_item).to_string(); - fn strip_serde_derives(item: syn::Item) -> (bool, bool, syn::Item) { + fn strip_serde_derives(item: syn::MacroInput) -> (bool, bool, syn::MacroInput) { let mut ser = false; let mut de = false; - let item = syn::Item { + let item = syn::MacroInput { attrs: item.attrs.into_iter().flat_map(|attr| { if attr.is_sugared_doc { return Some(attr); @@ -223,8 +223,8 @@ pub fn expand_single_item(item: &str) -> String { (ser, de, item) } - fn strip_serde_attrs(item: syn::Item) -> syn::Item { - syn::Item { + fn strip_serde_attrs(item: syn::MacroInput) -> syn::MacroInput { + syn::MacroInput { attrs: strip_serde_from_attrs(item.attrs), body: match item.body { syn::Body::Enum(variants) => syn::Body::Enum( @@ -233,6 +233,7 @@ pub fn expand_single_item(item: &str) -> String { ident: variant.ident, attrs: strip_serde_from_attrs(variant.attrs), data: strip_serde_from_variant_data(variant.data), + discriminant: variant.discriminant, } }).collect() ), diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 448c974c..3500a230 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -5,7 +5,7 @@ use bound; use internals::ast::{Body, Field, Item, Style, Variant}; use internals::{self, attr}; -pub fn expand_derive_serialize(item: &syn::Item) -> Tokens { +pub fn expand_derive_serialize(item: &syn::MacroInput) -> Tokens { let item = Item::from_ast(&internals::Ctxt::new(), item); let impl_generics = build_impl_generics(&item); diff --git a/serde_codegen_internals/Cargo.toml b/serde_codegen_internals/Cargo.toml index a364739b..f501786f 100644 --- a/serde_codegen_internals/Cargo.toml +++ b/serde_codegen_internals/Cargo.toml @@ -15,4 +15,4 @@ unstable-testing = ["clippy"] [dependencies] clippy = { version = "^0.*", optional = true } -syn = "0.5" +syn = "0.7" diff --git a/serde_codegen_internals/src/ast.rs b/serde_codegen_internals/src/ast.rs index d62ad807..2941c164 100644 --- a/serde_codegen_internals/src/ast.rs +++ b/serde_codegen_internals/src/ast.rs @@ -35,7 +35,7 @@ pub enum Style { } impl<'a> Item<'a> { - pub fn from_ast(cx: &Ctxt, item: &'a syn::Item) -> Item<'a> { + pub fn from_ast(cx: &Ctxt, item: &'a syn::MacroInput) -> Item<'a> { let attrs = attr::Item::from_ast(cx, item); let body = match item.body { diff --git a/serde_codegen_internals/src/attr.rs b/serde_codegen_internals/src/attr.rs index 02b72b6c..44da33e3 100644 --- a/serde_codegen_internals/src/attr.rs +++ b/serde_codegen_internals/src/attr.rs @@ -94,7 +94,7 @@ pub struct Item { impl Item { /// Extract out the `#[serde(...)]` attributes from an item. - pub fn from_ast(cx: &Ctxt, item: &syn::Item) -> Self { + pub fn from_ast(cx: &Ctxt, item: &syn::MacroInput) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields"); @@ -106,8 +106,10 @@ impl Item { match meta_item { // Parse `#[serde(rename="foo")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { - ser_name.set(lit.clone()); - de_name.set(lit.clone()); + if let Ok(s) = get_string_from_lit(cx, name, lit) { + ser_name.set(s.clone()); + de_name.set(s); + } } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` @@ -191,8 +193,10 @@ impl Variant { match meta_item { // Parse `#[serde(rename="foo")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { - ser_name.set(lit.clone()); - de_name.set(lit.clone()); + if let Ok(s) = get_string_from_lit(cx, name, lit) { + ser_name.set(s.clone()); + de_name.set(s); + } } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` @@ -275,8 +279,10 @@ impl Field { match meta_item { // Parse `#[serde(rename="foo")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { - ser_name.set(lit.clone()); - de_name.set(lit.clone()); + if let Ok(s) = get_string_from_lit(cx, name, lit) { + ser_name.set(s.clone()); + de_name.set(s); + } } // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` @@ -421,7 +427,7 @@ fn get_ser_and_de( items: &[syn::MetaItem], f: F ) -> Result, ()> - where F: Fn(&Ctxt, &Ident, &str) -> Result, + where F: Fn(&Ctxt, &Ident, &syn::Lit) -> Result, { let mut ser_item = Attr::none(cx, attribute); let mut de_item = Attr::none(cx, attribute); @@ -454,7 +460,7 @@ fn get_renames( cx: &Ctxt, items: &[syn::MetaItem], ) -> Result, ()> { - get_ser_and_de(cx, "rename", items, |_, _, s| Ok(s.to_owned())) + get_ser_and_de(cx, "rename", items, get_string_from_lit) } fn get_where_predicates( @@ -473,17 +479,28 @@ pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option> } } -fn parse_lit_into_path(cx: &Ctxt, name: &Ident, lit: &str) -> Result { - // TODO handle error - Ok(syn::parse_path(lit).unwrap()) +fn get_string_from_lit(_cx: &Ctxt, _name: &Ident, lit: &syn::Lit) -> Result { + if let syn::Lit::Str(ref s, _) = *lit { + Ok(s.clone()) + } else { + // TODO handle error + Err(()) + } } -fn parse_lit_into_where(cx: &Ctxt, name: &Ident, lit: &str) -> Result, ()> { - if lit.is_empty() { +fn parse_lit_into_path(cx: &Ctxt, name: &Ident, lit: &syn::Lit) -> Result { + let string = try!(get_string_from_lit(cx, name, lit)); + // TODO handle error + Ok(syn::parse_path(&string).unwrap()) +} + +fn parse_lit_into_where(cx: &Ctxt, name: &Ident, lit: &syn::Lit) -> Result, ()> { + let string = try!(get_string_from_lit(cx, name, lit)); + if string.is_empty() { return Ok(Vec::new()); } - let where_string = format!("where {}", lit); + let where_string = format!("where {}", string); // TODO handle error Ok(syn::parse_where_clause(&where_string).unwrap().predicates) From 8e865f62c4bfaa01c7b94c89487e8d06814d984f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 26 Sep 2016 22:13:53 -0700 Subject: [PATCH 05/15] Fix conflicts with the __serde_state commit --- serde_codegen/src/ser.rs | 120 +++++---------------------------------- 1 file changed, 14 insertions(+), 106 deletions(-) diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 7aad69a2..de27e912 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -164,25 +164,11 @@ fn serialize_tuple_struct( let len = serialize_stmts.len(); let let_mut = mut_if(len > 0); -<<<<<<< HEAD quote! { - let #let_mut state = try!(_serializer.serialize_tuple_struct(#type_name, #len)); + let #let_mut __serde_state = try!(_serializer.serialize_tuple_struct(#type_name, #len)); #(serialize_stmts)* - _serializer.serialize_tuple_struct_end(state) - } -||||||| merged common ancestors - quote_block!(cx, { - let $let_mut state = try!(_serializer.serialize_tuple_struct($type_name, $len)); - $serialize_stmts - _serializer.serialize_tuple_struct_end(state) - }).unwrap() -======= - quote_block!(cx, { - let $let_mut __serde_state = try!(_serializer.serialize_tuple_struct($type_name, $len)); - $serialize_stmts _serializer.serialize_tuple_struct_end(__serde_state) - }).unwrap() ->>>>>>> origin/master + } } fn serialize_struct( @@ -219,25 +205,11 @@ fn serialize_struct( }) .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); -<<<<<<< HEAD quote! { - let #let_mut state = try!(_serializer.serialize_struct(#type_name, #len)); + let #let_mut __serde_state = try!(_serializer.serialize_struct(#type_name, #len)); #(serialize_fields)* - _serializer.serialize_struct_end(state) - } -||||||| merged common ancestors - quote_block!(cx, { - let $let_mut state = try!(_serializer.serialize_struct($type_name, $len)); - $serialize_fields - _serializer.serialize_struct_end(state) - }).unwrap() -======= - quote_block!(cx, { - let $let_mut __serde_state = try!(_serializer.serialize_struct($type_name, $len)); - $serialize_fields _serializer.serialize_struct_end(__serde_state) - }).unwrap() ->>>>>>> origin/master + } } fn serialize_item_enum( @@ -401,29 +373,15 @@ fn serialize_tuple_variant( let len = serialize_stmts.len(); let let_mut = mut_if(len > 0); -<<<<<<< HEAD quote! { - let #let_mut state = try!(_serializer.serialize_tuple_variant(#type_name, #variant_index, #variant_name, #len)); + let #let_mut __serde_state = try!(_serializer.serialize_tuple_variant( + #type_name, + #variant_index, + #variant_name, + #len)); #(serialize_stmts)* - _serializer.serialize_tuple_variant_end(state) - } -||||||| merged common ancestors - quote_block!(cx, { - let $let_mut state = try!(_serializer.serialize_tuple_variant($type_name, $variant_index, $variant_name, $len)); - $serialize_stmts - _serializer.serialize_tuple_variant_end(state) - }).unwrap() -======= - quote_block!(cx, { - let $let_mut __serde_state = try!(_serializer.serialize_tuple_variant( - $type_name, - $variant_index, - $variant_name, - $len)); - $serialize_stmts _serializer.serialize_tuple_variant_end(__serde_state) - }).unwrap() ->>>>>>> origin/master + } } fn serialize_struct_variant( @@ -459,48 +417,18 @@ fn serialize_struct_variant( None => quote!(1), } }) -<<<<<<< HEAD .fold(quote!(0), |sum, expr| quote!(#sum + #expr)); quote! { - let #let_mut state = try!(_serializer.serialize_struct_variant( + let #let_mut __serde_state = try!(_serializer.serialize_struct_variant( #item_name, #variant_index, #variant_name, #len, -||||||| merged common ancestors - .fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr)); - - quote_block!(cx, { - let $let_mut state = try!(_serializer.serialize_struct_variant( - $item_name, - $variant_index, - $variant_name, - $len, -======= - .fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr)); - - quote_block!(cx, { - let $let_mut __serde_state = try!(_serializer.serialize_struct_variant( - $item_name, - $variant_index, - $variant_name, - $len, ->>>>>>> origin/master )); -<<<<<<< HEAD #(serialize_fields)* - _serializer.serialize_struct_variant_end(state) - } -||||||| merged common ancestors - $serialize_fields - _serializer.serialize_struct_variant_end(state) - }).unwrap() -======= - $serialize_fields _serializer.serialize_struct_variant_end(__serde_state) - }).unwrap() ->>>>>>> origin/master + } } fn serialize_tuple_struct_visitor( @@ -528,19 +456,9 @@ fn serialize_tuple_struct_visitor( &structure_ty, generics, &field.ty, path, field_expr); } -<<<<<<< HEAD let ser = quote! { - try!(_serializer.#func(&mut state, #field_expr)); + try!(_serializer.#func(&mut __serde_state, #field_expr)); }; -||||||| merged common ancestors - let ser = quote_expr!(cx, - try!(_serializer.$func(&mut state, $field_expr)); - ); -======= - let ser = quote_expr!(cx, - try!(_serializer.$func(&mut __serde_state, $field_expr)); - ); ->>>>>>> origin/master match skip { None => ser, @@ -577,19 +495,9 @@ fn serialize_struct_visitor( &structure_ty, generics, &field.ty, path, field_expr) } -<<<<<<< HEAD let ser = quote! { - try!(_serializer.#func(&mut state, #key_expr, #field_expr)); + try!(_serializer.#func(&mut __serde_state, #key_expr, #field_expr)); }; -||||||| merged common ancestors - let ser = quote_expr!(cx, - try!(_serializer.$func(&mut state, $key_expr, $field_expr)); - ); -======= - let ser = quote_expr!(cx, - try!(_serializer.$func(&mut __serde_state, $key_expr, $field_expr)); - ); ->>>>>>> origin/master match skip { None => ser, From 8e77960e3a525531df4182c246da6f1df5ccaefa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 26 Sep 2016 23:17:33 -0700 Subject: [PATCH 06/15] Remove with-libsyntax feature --- serde_codegen/Cargo.toml | 1 - serde_codegen/src/lib.rs | 12 +++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/serde_codegen/Cargo.toml b/serde_codegen/Cargo.toml index 132547be..52bc9554 100644 --- a/serde_codegen/Cargo.toml +++ b/serde_codegen/Cargo.toml @@ -18,7 +18,6 @@ with-syntex = [ "syntex", "syntex_syntax", ] -with-libsyntax = [] with-syn = [] [dependencies] diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index fed63700..01b26699 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -2,7 +2,7 @@ #![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "clippy", allow(too_many_arguments))] #![cfg_attr(feature = "clippy", allow(used_underscore_binding))] -#![cfg_attr(feature = "with-libsyntax", feature(rustc_private, plugin))] +#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))] // The `quote!` macro requires deep recursion. #![recursion_limit = "192"] @@ -16,11 +16,11 @@ extern crate syntex; #[macro_use] extern crate syntex_syntax as syntax; -#[cfg(feature = "with-libsyntax")] +#[cfg(not(feature = "with-syntex"))] #[macro_use] extern crate syntax; -#[cfg(feature = "with-libsyntax")] +#[cfg(not(feature = "with-syntex"))] extern crate rustc_plugin; extern crate syn; @@ -30,7 +30,7 @@ extern crate quote; #[cfg(feature = "with-syntex")] use std::path::Path; -#[cfg(feature = "with-libsyntax")] +#[cfg(not(feature = "with-syntex"))] use syntax::feature_gate::AttributeType; mod bound; @@ -104,7 +104,7 @@ pub fn expand(src: S, dst: D) -> Result<(), syntex::Error> syntex::with_extra_stack(expand_thread) } -#[cfg(feature = "with-libsyntax")] +#[cfg(not(feature = "with-syntex"))] pub fn register(reg: &mut rustc_plugin::Registry) { reg.register_syntax_extension( syntax::parse::token::intern("derive_Serialize"), @@ -156,9 +156,7 @@ macro_rules! shim { }; } -#[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")] From 8ee8c070900a1720a0acdd89bcf60ffaf2e46350 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 26 Sep 2016 23:17:43 -0700 Subject: [PATCH 07/15] Mark all serde attributes as used --- serde_codegen/src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index 01b26699..150360ac 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -140,6 +140,20 @@ macro_rules! shim { } }; + use syntax::{attr, ast, visit}; + struct MarkSerdeAttributesUsed; + impl visit::Visitor for MarkSerdeAttributesUsed { + fn visit_attribute(&mut self, attr: &ast::Attribute) { + match attr.node.value.node { + ast::MetaItemKind::List(ref name, _) if name == "serde" => { + attr::mark_used(attr); + } + _ => {} + } + } + } + visit::walk_item(&mut MarkSerdeAttributesUsed, item); + use syntax::print::pprust; let s = pprust::item_to_string(item); From 4ad6c4fd56c6d06b27f3fa4362622e6575771a4a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Sep 2016 00:02:15 -0700 Subject: [PATCH 08/15] Include unknown attribute name in error message --- serde_codegen/Cargo.toml | 2 +- serde_codegen_internals/Cargo.toml | 2 +- serde_codegen_internals/src/attr.rs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/serde_codegen/Cargo.toml b/serde_codegen/Cargo.toml index 52bc9554..0a4d6897 100644 --- a/serde_codegen/Cargo.toml +++ b/serde_codegen/Cargo.toml @@ -24,6 +24,6 @@ with-syn = [] clippy = { version = "^0.*", optional = true } quote = "0.1" serde_codegen_internals = { version = "=0.8.9", default-features = false, path = "../serde_codegen_internals" } -syn = { version = "0.7", features = ["aster", "visit"] } +syn = { version = "0.7.1", features = ["aster", "visit"] } syntex = { version = "^0.44.0", optional = true } syntex_syntax = { version = "^0.44.0", optional = true } diff --git a/serde_codegen_internals/Cargo.toml b/serde_codegen_internals/Cargo.toml index f501786f..30534fde 100644 --- a/serde_codegen_internals/Cargo.toml +++ b/serde_codegen_internals/Cargo.toml @@ -15,4 +15,4 @@ unstable-testing = ["clippy"] [dependencies] clippy = { version = "^0.*", optional = true } -syn = "0.7" +syn = "0.7.1" diff --git a/serde_codegen_internals/src/attr.rs b/serde_codegen_internals/src/attr.rs index 44da33e3..6ecbc8ad 100644 --- a/serde_codegen_internals/src/attr.rs +++ b/serde_codegen_internals/src/attr.rs @@ -142,8 +142,8 @@ impl Item { } _ => { - // TODO include name of attr - cx.error("unknown serde container attribute"); + cx.error(format!("unknown serde container attribute `{}`", + meta_item.name())); } } } @@ -208,8 +208,8 @@ impl Variant { } _ => { - // TODO include attribute - cx.error("unknown serde variant attribute"); + cx.error(format!("unknown serde variant attribute `{}`", + meta_item.name())); } } } @@ -353,8 +353,8 @@ impl Field { } _ => { - // TODO include attribute - cx.error("unknown serde field attribute"); + cx.error(format!("unknown serde field attribute `{}`", + meta_item.name())); } } } From 0c18c151e2d5186f22af69008629a20df687f6d8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Sep 2016 00:11:37 -0700 Subject: [PATCH 09/15] Revamp serde_codegen_internals error handling --- serde_codegen/src/de.rs | 7 +++--- serde_codegen/src/lib.rs | 18 ++++++++++----- serde_codegen/src/ser.rs | 10 +++++---- serde_codegen_internals/src/ctxt.rs | 34 ++++++++++++++++++++--------- serde_derive/src/lib.rs | 12 ++++++---- 5 files changed, 54 insertions(+), 27 deletions(-) diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index d8a147d0..680c534b 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -5,11 +5,12 @@ use bound; use internals::ast::{Body, Field, Item, Style, Variant}; use internals::{self, attr}; -pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Tokens { +pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Result { let item = { let ctxt = internals::Ctxt::new(); let item = Item::from_ast(&ctxt, item); check_no_str(&ctxt, &item); + try!(ctxt.check()); item }; @@ -27,7 +28,7 @@ pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Tokens { let dummy_const = aster::id(format!("_IMPL_DESERIALIZE_FOR_{}", item.ident)); - quote! { + Ok(quote! { #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] const #dummy_const: () = { extern crate serde as _serde; @@ -40,7 +41,7 @@ pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Tokens { } } }; - } + }) } // All the generics in the input, plus a bound `T: Deserialize` for each generic diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index 150360ac..9656016d 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -123,7 +123,7 @@ macro_rules! shim { ($name:ident $pkg:ident :: $func:ident) => { fn $func( cx: &mut ::syntax::ext::base::ExtCtxt, - _span: ::syntax::codemap::Span, + span: ::syntax::codemap::Span, meta_item: &::syntax::ast::MetaItem, annotatable: &::syntax::ext::base::Annotatable, push: &mut FnMut(::syntax::ext::base::Annotatable) @@ -158,7 +158,13 @@ macro_rules! shim { let s = pprust::item_to_string(item); let syn_item = syn::parse_macro_input(&s).unwrap(); - let expanded = $pkg::$func(&syn_item).to_string(); + let expanded = match $pkg::$func(&syn_item) { + Ok(expanded) => expanded.to_string(), + Err(msg) => { + cx.span_err(span, &msg); + return; + } + }; use syntax::parse; let name = stringify!($name).to_string(); @@ -174,21 +180,21 @@ shim!(Serialize ser::expand_derive_serialize); shim!(Deserialize de::expand_derive_deserialize); #[cfg(feature = "with-syn")] -pub fn expand_single_item(item: &str) -> String { +pub fn expand_single_item(item: &str) -> Result { let syn_item = syn::parse_macro_input(item).unwrap(); let (ser, de, syn_item) = strip_serde_derives(syn_item); let expanded_ser = if ser { - Some(ser::expand_derive_serialize(&syn_item)) + Some(try!(ser::expand_derive_serialize(&syn_item))) } else { None }; let expanded_de = if de { - Some(de::expand_derive_deserialize(&syn_item)) + Some(try!(de::expand_derive_deserialize(&syn_item))) } else { None:: }; let syn_item = strip_serde_attrs(syn_item); - return quote!(#expanded_ser #expanded_de #syn_item).to_string(); + return Ok(quote!(#expanded_ser #expanded_de #syn_item).to_string()); fn strip_serde_derives(item: syn::MacroInput) -> (bool, bool, syn::MacroInput) { let mut ser = false; diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index de27e912..08aebb83 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -5,8 +5,10 @@ use bound; use internals::ast::{Body, Field, Item, Style, Variant}; use internals::{self, attr}; -pub fn expand_derive_serialize(item: &syn::MacroInput) -> Tokens { - let item = Item::from_ast(&internals::Ctxt::new(), item); +pub fn expand_derive_serialize(item: &syn::MacroInput) -> Result { + let ctxt = internals::Ctxt::new(); + let item = Item::from_ast(&ctxt, item); + try!(ctxt.check()); let impl_generics = build_impl_generics(&item); @@ -22,7 +24,7 @@ pub fn expand_derive_serialize(item: &syn::MacroInput) -> Tokens { let dummy_const = aster::id(format!("_IMPL_SERIALIZE_FOR_{}", item.ident)); - quote! { + Ok(quote! { #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] const #dummy_const: () = { extern crate serde as _serde; @@ -35,7 +37,7 @@ pub fn expand_derive_serialize(item: &syn::MacroInput) -> Tokens { } } }; - } + }) } // All the generics in the input, plus a bound `T: Serialize` for each generic diff --git a/serde_codegen_internals/src/ctxt.rs b/serde_codegen_internals/src/ctxt.rs index a516aec8..55ec5582 100644 --- a/serde_codegen_internals/src/ctxt.rs +++ b/serde_codegen_internals/src/ctxt.rs @@ -1,29 +1,43 @@ use std::fmt::Display; -use std::cell::Cell; +use std::cell::RefCell; #[derive(Default)] pub struct Ctxt { - err_count: Cell, + errors: RefCell>>, } impl Ctxt { pub fn new() -> Self { - Default::default() + Ctxt { + errors: RefCell::new(Some(Vec::new())), + } } pub fn error(&self, msg: T) { - println!("{}", msg); - self.err_count.set(self.err_count.get() + 1); + self.errors.borrow_mut().as_mut().unwrap().push(msg.to_string()); + } + + pub fn check(self) -> Result<(), String> { + let mut errors = self.errors.borrow_mut().take().unwrap(); + match errors.len() { + 0 => Ok(()), + 1 => Err(errors.pop().unwrap()), + n => { + let mut msg = format!("{} errors:", n); + for err in errors { + msg += "\n\t# "; + msg += &err; + } + Err(msg) + } + } } } impl Drop for Ctxt { fn drop(&mut self) { - let err_count = self.err_count.get(); - if err_count == 1 { - panic!("1 error"); - } else if err_count > 1 { - panic!("{} errors", err_count); + if self.errors.borrow().is_some() { + panic!("forgot to check for errors"); } } } diff --git a/serde_derive/src/lib.rs b/serde_derive/src/lib.rs index 5c80708a..0f435142 100644 --- a/serde_derive/src/lib.rs +++ b/serde_derive/src/lib.rs @@ -9,13 +9,17 @@ use rustc_macro::TokenStream; #[rustc_macro_derive(Serialize)] pub fn derive_serialize(input: TokenStream) -> TokenStream { let item = format!("#[derive(Serialize)]\n{}", input); - let expanded = serde_codegen::expand_single_item(&item); - expanded.parse().unwrap() + match serde_codegen::expand_single_item(&item) { + Ok(expanded) => expanded.parse().unwrap(), + Err(msg) => panic!(msg), + } } #[rustc_macro_derive(Deserialize)] pub fn derive_deserialize(input: TokenStream) -> TokenStream { let item = format!("#[derive(Deserialize)]\n{}", input); - let expanded = serde_codegen::expand_single_item(&item); - expanded.parse().unwrap() + match serde_codegen::expand_single_item(&item) { + Ok(expanded) => expanded.parse().unwrap(), + Err(msg) => panic!(msg), + } } From 40b874214a3a9b64181211b3fa28f290ca973366 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Sep 2016 00:17:00 -0700 Subject: [PATCH 10/15] Update tests to new location of errors --- .../tests/compile-fail/duplicate_attributes.rs | 14 +++++++------- .../compile-fail/reject-unknown-attributes.rs | 16 ++++++++-------- serde_macros/tests/compile-fail/str_ref_deser.rs | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/serde_macros/tests/compile-fail/duplicate_attributes.rs b/serde_macros/tests/compile-fail/duplicate_attributes.rs index 4c1caab2..61c98697 100644 --- a/serde_macros/tests/compile-fail/duplicate_attributes.rs +++ b/serde_macros/tests/compile-fail/duplicate_attributes.rs @@ -1,14 +1,14 @@ #![feature(custom_attribute, custom_derive, plugin)] #![plugin(serde_macros)] -#[derive(Serialize)] +#[derive(Serialize)] //~ ERROR: 6 errors: struct S { #[serde(rename(serialize="x"))] - #[serde(rename(serialize="y"))] //~ ERROR: duplicate serde attribute `rename` + #[serde(rename(serialize="y"))] // ERROR: duplicate serde attribute `rename` a: (), #[serde(rename(serialize="x"))] - #[serde(rename="y")] //~ ERROR: duplicate serde attribute `rename` + #[serde(rename="y")] // ERROR: duplicate serde attribute `rename` b: (), #[serde(rename(serialize="x"))] @@ -16,16 +16,16 @@ struct S { c: (), #[serde(rename="x")] - #[serde(rename(deserialize="y"))] //~ ERROR: duplicate serde attribute `rename` + #[serde(rename(deserialize="y"))] // ERROR: duplicate serde attribute `rename` d: (), - #[serde(rename(serialize="x", serialize="y"))] //~ ERROR: duplicate serde attribute `rename` + #[serde(rename(serialize="x", serialize="y"))] // ERROR: duplicate serde attribute `rename` e: (), - #[serde(rename="x", serialize="y")] //~ ERROR: unknown serde field attribute `serialize = "y"` + #[serde(rename="x", serialize="y")] // ERROR: unknown serde field attribute `serialize` f: (), - #[serde(rename(serialize="x"), rename(serialize="y"))] //~ ERROR: duplicate serde attribute `rename` + #[serde(rename(serialize="x"), rename(serialize="y"))] // ERROR: duplicate serde attribute `rename` g: (), } diff --git a/serde_macros/tests/compile-fail/reject-unknown-attributes.rs b/serde_macros/tests/compile-fail/reject-unknown-attributes.rs index 5a4d2776..9c71fae7 100644 --- a/serde_macros/tests/compile-fail/reject-unknown-attributes.rs +++ b/serde_macros/tests/compile-fail/reject-unknown-attributes.rs @@ -3,27 +3,27 @@ extern crate serde; -#[derive(Serialize)] -#[serde(abc="xyz")] //~ unknown serde container attribute `abc = "xyz"` +#[derive(Serialize)] //~ unknown serde container attribute `abc` +#[serde(abc="xyz")] struct Foo { x: u32, } -#[derive(Deserialize)] -#[serde(abc="xyz")] //~ unknown serde container attribute `abc = "xyz"` +#[derive(Deserialize)] //~ unknown serde container attribute `abc` +#[serde(abc="xyz")] struct Foo { x: u32, } -#[derive(Serialize)] +#[derive(Serialize)] //~ unknown serde field attribute `abc` struct Foo { - #[serde(abc="xyz")] //~ unknown serde field attribute `abc = "xyz"` + #[serde(abc="xyz")] x: u32, } -#[derive(Deserialize)] +#[derive(Deserialize)] //~ unknown serde field attribute `abc` struct Foo { - #[serde(abc="xyz")] //~ unknown serde field attribute `abc = "xyz"` + #[serde(abc="xyz")] x: u32, } diff --git a/serde_macros/tests/compile-fail/str_ref_deser.rs b/serde_macros/tests/compile-fail/str_ref_deser.rs index 827d08c9..610ed680 100644 --- a/serde_macros/tests/compile-fail/str_ref_deser.rs +++ b/serde_macros/tests/compile-fail/str_ref_deser.rs @@ -1,9 +1,9 @@ #![feature(custom_attribute, custom_derive, plugin)] #![plugin(serde_macros)] -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize)] //~ ERROR: Serde does not support deserializing fields of type &str struct Test<'a> { - s: &'a str, //~ ERROR: Serde does not support deserializing fields of type &str + s: &'a str, } fn main() {} From 7e441e5110d69da8751d6480ccd4c7c74b6d7334 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Sep 2016 00:40:37 -0700 Subject: [PATCH 11/15] Handle various attribute parsing error cases --- serde_codegen_internals/src/attr.rs | 54 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/serde_codegen_internals/src/attr.rs b/serde_codegen_internals/src/attr.rs index 6ecbc8ad..2f90328d 100644 --- a/serde_codegen_internals/src/attr.rs +++ b/serde_codegen_internals/src/attr.rs @@ -1,5 +1,5 @@ use Ctxt; -use syn::{self, Ident}; +use syn; // This module handles parsing of `#[serde(...)]` attributes. The entrypoints // are `attr::Item::from_ast`, `attr::Variant::from_ast`, and @@ -106,7 +106,7 @@ impl Item { match meta_item { // Parse `#[serde(rename="foo")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { - if let Ok(s) = get_string_from_lit(cx, name, lit) { + if let Ok(s) = get_string_from_lit(cx, name.as_ref(), name.as_ref(), lit) { ser_name.set(s.clone()); de_name.set(s); } @@ -127,7 +127,7 @@ impl Item { // Parse `#[serde(bound="D: Serialize")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "bound" => { - if let Ok(where_predicates) = parse_lit_into_where(cx, name, lit) { + if let Ok(where_predicates) = parse_lit_into_where(cx, name.as_ref(), name.as_ref(), lit) { ser_bound.set(where_predicates.clone()); de_bound.set(where_predicates); } @@ -193,7 +193,7 @@ impl Variant { match meta_item { // Parse `#[serde(rename="foo")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { - if let Ok(s) = get_string_from_lit(cx, name, lit) { + if let Ok(s) = get_string_from_lit(cx, name.as_ref(), name.as_ref(), lit) { ser_name.set(s.clone()); de_name.set(s); } @@ -279,7 +279,7 @@ impl Field { match meta_item { // Parse `#[serde(rename="foo")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "rename" => { - if let Ok(s) = get_string_from_lit(cx, name, lit) { + if let Ok(s) = get_string_from_lit(cx, name.as_ref(), name.as_ref(), lit) { ser_name.set(s.clone()); de_name.set(s); } @@ -300,7 +300,7 @@ impl Field { // Parse `#[serde(default="...")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "default" => { - if let Ok(path) = parse_lit_into_path(cx, name, lit) { + if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) { default.set(FieldDefault::Path(path)); } } @@ -317,28 +317,28 @@ impl Field { // Parse `#[serde(skip_serializing_if="...")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "skip_serializing_if" => { - if let Ok(path) = parse_lit_into_path(cx, name, lit) { + if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) { skip_serializing_if.set(path); } } // Parse `#[serde(serialize_with="...")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "serialize_with" => { - if let Ok(path) = parse_lit_into_path(cx, name, lit) { + if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) { serialize_with.set(path); } } // Parse `#[serde(deserialize_with="...")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "deserialize_with" => { - if let Ok(path) = parse_lit_into_path(cx, name, lit) { + if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) { deserialize_with.set(path); } } // Parse `#[serde(bound="D: Serialize")]` syn::MetaItem::NameValue(ref name, ref lit) if name == "bound" => { - if let Ok(where_predicates) = parse_lit_into_where(cx, name, lit) { + if let Ok(where_predicates) = parse_lit_into_where(cx, name.as_ref(), name.as_ref(), lit) { ser_bound.set(where_predicates.clone()); de_bound.set(where_predicates); } @@ -423,31 +423,32 @@ type SerAndDe = (Option, Option); fn get_ser_and_de( cx: &Ctxt, - attribute: &'static str, + attr_name: &'static str, items: &[syn::MetaItem], f: F ) -> Result, ()> - where F: Fn(&Ctxt, &Ident, &syn::Lit) -> Result, + where F: Fn(&Ctxt, &str, &str, &syn::Lit) -> Result, { - let mut ser_item = Attr::none(cx, attribute); - let mut de_item = Attr::none(cx, attribute); + let mut ser_item = Attr::none(cx, attr_name); + let mut de_item = Attr::none(cx, attr_name); for item in items { match *item { syn::MetaItem::NameValue(ref name, ref lit) if name == "serialize" => { - if let Ok(v) = f(cx, name, lit) { + if let Ok(v) = f(cx, attr_name, name.as_ref(), lit) { ser_item.set(v); } } syn::MetaItem::NameValue(ref name, ref lit) if name == "deserialize" => { - if let Ok(v) = f(cx, name, lit) { + if let Ok(v) = f(cx, attr_name, name.as_ref(), lit) { de_item.set(v); } } _ => { - cx.error(format!("bad {} attribute", attribute)); + cx.error(format!("malformed {0} attribute, expected `{0}(serialize = ..., deserialize = ...)`", + attr_name)); return Err(()); } } @@ -479,29 +480,28 @@ pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option> } } -fn get_string_from_lit(_cx: &Ctxt, _name: &Ident, lit: &syn::Lit) -> Result { +fn get_string_from_lit(cx: &Ctxt, attr_name: &str, meta_item_name: &str, lit: &syn::Lit) -> Result { if let syn::Lit::Str(ref s, _) = *lit { Ok(s.clone()) } else { - // TODO handle error + cx.error(format!("expected serde {} attribute to be a string: `{} = \"...\"`", + attr_name, meta_item_name)); Err(()) } } -fn parse_lit_into_path(cx: &Ctxt, name: &Ident, lit: &syn::Lit) -> Result { - let string = try!(get_string_from_lit(cx, name, lit)); - // TODO handle error - Ok(syn::parse_path(&string).unwrap()) +fn parse_lit_into_path(cx: &Ctxt, attr_name: &str, lit: &syn::Lit) -> Result { + let string = try!(get_string_from_lit(cx, attr_name, attr_name, lit)); + syn::parse_path(&string).map_err(|err| cx.error(err)) } -fn parse_lit_into_where(cx: &Ctxt, name: &Ident, lit: &syn::Lit) -> Result, ()> { - let string = try!(get_string_from_lit(cx, name, lit)); +fn parse_lit_into_where(cx: &Ctxt, attr_name: &str, meta_item_name: &str, lit: &syn::Lit) -> Result, ()> { + let string = try!(get_string_from_lit(cx, attr_name, meta_item_name, lit)); if string.is_empty() { return Ok(Vec::new()); } let where_string = format!("where {}", string); - // TODO handle error - Ok(syn::parse_where_clause(&where_string).unwrap().predicates) + syn::parse_where_clause(&where_string).map(|wh| wh.predicates).map_err(|err| cx.error(err)) } From 7d09b1475c88c2890f194b05af6319a27bf72504 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Sep 2016 00:46:03 -0700 Subject: [PATCH 12/15] Fix clippy lints in serde_codegen --- serde_codegen/src/de.rs | 21 ++++++++++----------- serde_codegen/src/ser.rs | 10 +++++----- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 680c534b..dd97407f 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -62,10 +62,9 @@ fn build_impl_generics(item: &Item) -> syn::Generics { let generics = bound::with_bound(item, &generics, needs_deserialize_bound, &aster::path().ids(&["_serde", "de", "Deserialize"]).build()); - let generics = bound::with_bound(item, &generics, + bound::with_bound(item, &generics, requires_default, - &aster::path().global().ids(&["std", "default", "Default"]).build()); - generics + &aster::path().global().ids(&["std", "default", "Default"]).build()) } } } @@ -325,7 +324,7 @@ fn deserialize_seq( } Some(path) => { let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with( - type_ident, impl_generics, &field.ty, path); + type_ident, impl_generics, field.ty, path); quote!({ #wrapper #wrapper_impl @@ -390,7 +389,7 @@ fn deserialize_newtype_struct( } Some(path) => { let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with( - type_ident, impl_generics, &field.ty, path); + type_ident, impl_generics, field.ty, path); quote!({ #wrapper #wrapper_impl @@ -421,7 +420,7 @@ fn deserialize_struct( let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(impl_generics); let type_path = match variant_ident { - Some(ref variant_ident) => quote!(#type_ident::#variant_ident), + Some(variant_ident) => quote!(#type_ident::#variant_ident), None => quote!(#type_ident), }; @@ -580,7 +579,7 @@ fn deserialize_variant( Style::Newtype => { deserialize_newtype_variant( type_ident, - &variant_ident, + variant_ident, generics, &variant.fields[0], ) @@ -621,7 +620,7 @@ fn deserialize_newtype_variant( } Some(path) => { let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with( - type_ident, impl_generics, &field.ty, path); + type_ident, impl_generics, field.ty, path); quote!({ #wrapper #wrapper_impl @@ -719,7 +718,7 @@ fn deserialize_field_visitor( // Match arms to extract a field from a string let bytes_field_arms: Vec<_> = field_idents.iter().zip(field_names.iter()) .map(|(field_ident, field_name)| { - let bytes_field_name = quote::ByteStr(&field_name); + let bytes_field_name = quote::ByteStr(field_name); quote! { #bytes_field_name => { Ok(__Field::#field_ident) } } @@ -849,7 +848,7 @@ fn deserialize_map( // Match arms to extract a value for a field. let value_arms = fields_names.iter() .filter(|&&(field, _)| !field.attrs.skip_deserializing()) - .map(|&(ref field, ref name)| { + .map(|&(field, ref name)| { let deser_name = field.attrs.name().deserialize_name(); let visit = match field.attrs.deserialize_with() { @@ -861,7 +860,7 @@ fn deserialize_map( } Some(path) => { let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with( - type_ident, impl_generics, &field.ty, path); + type_ident, impl_generics, field.ty, path); quote!({ #wrapper #wrapper_impl diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 08aebb83..bb1c1089 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -140,7 +140,7 @@ fn serialize_newtype_struct( let mut field_expr = quote!(&self.0); if let Some(path) = field.attrs.serialize_with() { field_expr = wrap_serialize_with( - &item_ty, impl_generics, &field.ty, path, field_expr); + &item_ty, impl_generics, field.ty, path, field_expr); } quote! { @@ -342,7 +342,7 @@ fn serialize_newtype_variant( let mut field_expr = quote!(__simple_value); if let Some(path) = field.attrs.serialize_with() { field_expr = wrap_serialize_with( - &item_ty, generics, &field.ty, path, field_expr); + &item_ty, generics, field.ty, path, field_expr); } quote! { @@ -397,7 +397,7 @@ fn serialize_struct_variant( let serialize_fields = serialize_struct_visitor( ty.clone(), fields, - &generics, + generics, true, aster::id("serialize_struct_variant_elt"), ); @@ -455,7 +455,7 @@ fn serialize_tuple_struct_visitor( if let Some(path) = field.attrs.serialize_with() { field_expr = wrap_serialize_with( - &structure_ty, generics, &field.ty, path, field_expr); + &structure_ty, generics, field.ty, path, field_expr); } let ser = quote! { @@ -494,7 +494,7 @@ fn serialize_struct_visitor( if let Some(path) = field.attrs.serialize_with() { field_expr = wrap_serialize_with( - &structure_ty, generics, &field.ty, path, field_expr) + &structure_ty, generics, field.ty, path, field_expr) } let ser = quote! { From 9a86e6818f2e3c92ffb15b2fd0e768243bfc4d43 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Sep 2016 00:47:54 -0700 Subject: [PATCH 13/15] Use push_str to support old compilers --- serde_codegen_internals/src/ctxt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde_codegen_internals/src/ctxt.rs b/serde_codegen_internals/src/ctxt.rs index 55ec5582..a1455332 100644 --- a/serde_codegen_internals/src/ctxt.rs +++ b/serde_codegen_internals/src/ctxt.rs @@ -25,8 +25,8 @@ impl Ctxt { n => { let mut msg = format!("{} errors:", n); for err in errors { - msg += "\n\t# "; - msg += &err; + msg.push_str("\n\t# "); + msg.push_str(&err); } Err(msg) } From effa298871f5a21231fa5c68b24d92cc70c67f3a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 27 Sep 2016 01:04:11 -0700 Subject: [PATCH 14/15] Fix differences in the generated code --- serde_codegen/Cargo.toml | 4 ++-- serde_codegen/src/de.rs | 31 +++++++++++++++++------------- serde_codegen/src/ser.rs | 21 +++++++++++++++----- serde_codegen_internals/Cargo.toml | 2 +- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/serde_codegen/Cargo.toml b/serde_codegen/Cargo.toml index 0a4d6897..56eb6bcc 100644 --- a/serde_codegen/Cargo.toml +++ b/serde_codegen/Cargo.toml @@ -22,8 +22,8 @@ with-syn = [] [dependencies] clippy = { version = "^0.*", optional = true } -quote = "0.1" +quote = "0.2" serde_codegen_internals = { version = "=0.8.9", default-features = false, path = "../serde_codegen_internals" } -syn = { version = "0.7.1", features = ["aster", "visit"] } +syn = { version = "0.8", features = ["aster", "visit"] } syntex = { version = "^0.44.0", optional = true } syntex_syntax = { version = "^0.44.0", optional = true } diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index dd97407f..398f6422 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -36,9 +36,7 @@ pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Result(deserializer: &mut __D) -> ::std::result::Result<#ty, __D::Error> where __D: _serde::de::Deserializer - { - #body - } + #body } }; }) @@ -151,6 +149,8 @@ fn deserialize_visitor(generics: &syn::Generics) -> (Tokens, Tokens, Tokens) { quote!(__Visitor), ) } else { + let where_clause = &generics.where_clause; + let num_phantoms = generics.lifetimes.len() + generics.ty_params.len(); let phantom_types = generics.lifetimes.iter() @@ -190,7 +190,7 @@ fn deserialize_visitor(generics: &syn::Generics) -> (Tokens, Tokens, Tokens) { ( quote! { - struct __Visitor #generics ( #(phantom_types),* ); + struct __Visitor #generics ( #(phantom_types),* ) #where_clause; }, quote!(__Visitor <#(all_params),*> ), quote!(__Visitor #ty_param_idents ( #(phantom_exprs),* )), @@ -204,7 +204,7 @@ fn deserialize_unit_struct( ) -> Tokens { let type_name = item_attrs.name().deserialize_name(); - quote! { + quote!({ struct __Visitor; impl _serde::de::Visitor for __Visitor { @@ -227,7 +227,7 @@ fn deserialize_unit_struct( } deserializer.deserialize_unit_struct(#type_name, __Visitor) - } + }) } fn deserialize_tuple( @@ -537,7 +537,7 @@ fn deserialize_item_enum( let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(impl_generics); - quote! { + quote!({ #variant_visitor #visitor_item @@ -557,7 +557,7 @@ fn deserialize_item_enum( #variants_stmt deserializer.deserialize_enum(#type_name, VARIANTS, #visitor_expr) - } + }) } fn deserialize_variant( @@ -628,9 +628,11 @@ fn deserialize_newtype_variant( }) } }; - quote! { - Ok(#type_ident::#variant_ident(#visit)), - } + // The outer braces are unnecessary but quasi used to have them. We can + // remove them separately from the syn conversion. + quote!({ + Ok(#type_ident::#variant_ident(#visit)) + }) } fn deserialize_field_visitor( @@ -861,11 +863,14 @@ fn deserialize_map( Some(path) => { let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with( type_ident, impl_generics, field.ty, path); - quote!({ + // The outer parentheses are redundant but quasi used to put + // them in. We can remove them separately from the syn + // conversion. + quote!(({ #wrapper #wrapper_impl try!(visitor.visit_value::<#wrapper_ty>()).value - }) + })) } }; quote! { diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index bb1c1089..57d85fb7 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -279,7 +279,9 @@ fn serialize_variant( ); quote! { - #type_ident::#variant_ident(ref __simple_value) => #block, + // The braces are unnecessary but quasi used to put them in. We + // can remove them separately from the syn conversion. + #type_ident::#variant_ident(ref __simple_value) => { #block }, } }, Style::Tuple => { @@ -447,6 +449,7 @@ fn serialize_tuple_struct_visitor( let id = aster::id(format!("__field{}", i)); quote!(#id) } else { + let i = aster::id(i); quote!(&self.#i) }; @@ -459,11 +462,15 @@ fn serialize_tuple_struct_visitor( } let ser = quote! { - try!(_serializer.#func(&mut __serde_state, #field_expr)); + // This line should end in a semicolon but quasi used to behave + // differently between skipped and non-skipped so I have + // preserved that behavior. We can update it separately from the + // syn conversion. + try!(_serializer.#func(&mut __serde_state, #field_expr)) }; match skip { - None => ser, + None => quote!(#ser;), Some(skip) => quote!(if !#skip { #ser }), } }) @@ -498,11 +505,15 @@ fn serialize_struct_visitor( } let ser = quote! { - try!(_serializer.#func(&mut __serde_state, #key_expr, #field_expr)); + // This line should end in a semicolon but quasi used to behave + // differently between skipped and non-skipped so I have + // preserved that behavior. We can update it separately from the + // syn conversion. + try!(_serializer.#func(&mut __serde_state, #key_expr, #field_expr)) }; match skip { - None => ser, + None => quote!(#ser;), Some(skip) => quote!(if !#skip { #ser }), } }) diff --git a/serde_codegen_internals/Cargo.toml b/serde_codegen_internals/Cargo.toml index 30534fde..8d14d7bf 100644 --- a/serde_codegen_internals/Cargo.toml +++ b/serde_codegen_internals/Cargo.toml @@ -15,4 +15,4 @@ unstable-testing = ["clippy"] [dependencies] clippy = { version = "^0.*", optional = true } -syn = "0.7.1" +syn = "0.8" From 9fe16767c53407d82a389ccabaeaa29891c10065 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 28 Sep 2016 08:57:53 -0700 Subject: [PATCH 15/15] Use iter::repeat to build phantom exprs --- serde_codegen/src/de.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 398f6422..dfe84398 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -5,6 +5,8 @@ use bound; use internals::ast::{Body, Field, Item, Style, Variant}; use internals::{self, attr}; +use std::iter; + pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Result { let item = { let ctxt = internals::Ctxt::new(); @@ -186,7 +188,7 @@ fn deserialize_visitor(generics: &syn::Generics) -> (Tokens, Tokens, Tokens) { Some(quote!(::<#(ty_param_idents),*>)) }; - let phantom_exprs = (0 .. num_phantoms).map(|_| quote!(::std::marker::PhantomData)); + let phantom_exprs = iter::repeat(quote!(::std::marker::PhantomData)).take(num_phantoms); ( quote! {