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