diff --git a/serde_codegen/Cargo.toml b/serde_codegen/Cargo.toml index 9a9a6831..ad469496 100644 --- a/serde_codegen/Cargo.toml +++ b/serde_codegen/Cargo.toml @@ -14,7 +14,14 @@ include = ["Cargo.toml", "build.rs", "src/**/*.rs", "src/lib.rs.in"] default = ["with-syntex"] nightly = ["quasi_macros"] nightly-testing = ["clippy"] -with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"] +with-syntex = [ + "quasi/with-syntex", + "quasi_codegen", + "quasi_codegen/with-syntex", + "serde_item/with-syntex", + "syntex", + "syntex_syntax", +] [build-dependencies] quasi_codegen = { version = "^0.12.0", optional = true } @@ -25,5 +32,6 @@ aster = { version = "^0.18.0", default-features = false } clippy = { version = "^0.*", optional = true } quasi = { version = "^0.12.0", default-features = false } quasi_macros = { version = "^0.12.0", optional = true } +serde_item = { version = "0.1", path = "../serde_item", default-features = false } syntex = { version = "^0.35.0", optional = true } syntex_syntax = { version = "^0.35.0", optional = true } diff --git a/serde_codegen/src/bound.rs b/serde_codegen/src/bound.rs index a1dbf90f..0acbcab8 100644 --- a/serde_codegen/src/bound.rs +++ b/serde_codegen/src/bound.rs @@ -6,8 +6,7 @@ use syntax::ast; use syntax::ptr::P; use syntax::visit; -use attr; -use item::Item; +use item::{attr, Item}; // Remove the default from every type parameter because in the generated impls // they look like associated types: "error: associated type bindings are not @@ -39,7 +38,7 @@ pub fn with_where_predicates_from_fields( generics: &ast::Generics, from_field: F, ) -> ast::Generics - where F: Fn(&attr::FieldAttrs) -> Option<&[ast::WherePredicate]>, + where F: Fn(&attr::Field) -> Option<&[ast::WherePredicate]>, { builder.from_generics(generics.clone()) .with_predicates( @@ -56,7 +55,7 @@ pub fn with_bound( filter: F, bound: &ast::Path, ) -> ast::Generics - where F: Fn(&attr::FieldAttrs) -> bool, + where F: Fn(&attr::Field) -> bool, { builder.from_generics(generics.clone()) .with_predicates( diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index d589bfda..175d7c22 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -6,10 +6,9 @@ use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::parse::token::InternedString; use syntax::ptr::P; -use attr; use bound; use error::Error; -use item; +use item::{self, attr}; pub fn expand_derive_deserialize( cx: &mut ExtCtxt, @@ -28,27 +27,30 @@ pub fn expand_derive_deserialize( } }; - let builder = aster::AstBuilder::new().span(span); - - let impl_item = match deserialize_item(cx, &builder, &item) { + let item = match item::Item::from_ast(cx, item) { Ok(item) => item, - Err(Error) => { - // An error occured, but it should have been reported already. + Err(item::Error::UnexpectedItemKind) => { + cx.span_err(item.span, + "`#[derive(Deserialize)]` may only be applied to structs and enums"); return; } }; + if check_no_str(cx, &item).is_err() { + return; + } + + let builder = aster::AstBuilder::new().span(span); + + let impl_item = deserialize_item(cx, &builder, &item); push(Annotatable::Item(impl_item)) } fn deserialize_item( cx: &ExtCtxt, builder: &aster::AstBuilder, - item: &ast::Item, -) -> Result, Error> { - let item = try!(item::Item::from_ast(cx, "Deserialize", item)); - try!(check_no_str(cx, &item)); - + item: &item::Item, +) -> P { let impl_generics = build_impl_generics(builder, &item); let ty = builder.ty().path() @@ -65,7 +67,7 @@ fn deserialize_item( let dummy_const = builder.id(format!("_IMPL_DESERIALIZE_FOR_{}", item.ident)); - Ok(quote_item!(cx, + quote_item!(cx, #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] const $dummy_const: () = { extern crate serde as _serde; @@ -78,7 +80,7 @@ fn deserialize_item( } } }; - ).unwrap()) + ).unwrap() } // All the generics in the input, plus a bound `T: Deserialize` for each generic @@ -114,7 +116,7 @@ fn build_impl_generics( // deserialized by us so we do not generate a bound. Fields with a `bound` // attribute specify their own bound so we do not generate one. All other fields // may need a `T: Deserialize` bound where T is the type of the field. -fn needs_deserialize_bound(attrs: &attr::FieldAttrs) -> bool { +fn needs_deserialize_bound(attrs: &attr::Field) -> bool { !attrs.skip_deserializing() && attrs.deserialize_with().is_none() && attrs.de_bound().is_none() @@ -122,7 +124,7 @@ fn needs_deserialize_bound(attrs: &attr::FieldAttrs) -> bool { // Fields with a `default` attribute (not `default=...`), and fields with a // `skip_deserializing` attribute that do not also have `default=...`. -fn requires_default(attrs: &attr::FieldAttrs) -> bool { +fn requires_default(attrs: &attr::Field) -> bool { attrs.default() == &attr::FieldDefault::Default } @@ -178,6 +180,7 @@ fn deserialize_body( item::Body::Struct(item::Style::Unit, _) => { deserialize_unit_struct( cx, + builder, item.ident, &item.attrs) } @@ -266,10 +269,11 @@ fn deserializer_ty_arg(builder: &aster::AstBuilder) -> P{ fn deserialize_unit_struct( cx: &ExtCtxt, + builder: &aster::AstBuilder, type_ident: Ident, - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { - let type_name = container_attrs.name().deserialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); quote_expr!(cx, { struct __Visitor; @@ -305,7 +309,7 @@ fn deserialize_tuple( impl_generics: &ast::Generics, ty: P, fields: &[item::Field], - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { let where_clause = &impl_generics.where_clause; @@ -351,11 +355,11 @@ fn deserialize_tuple( quote_expr!(cx, visitor.visit_tuple($nfields, $visitor_expr)) } else if nfields == 1 { - let type_name = container_attrs.name().deserialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); quote_expr!(cx, deserializer.deserialize_newtype_struct($type_name, $visitor_expr)) } else { - let type_name = container_attrs.name().deserialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); quote_expr!(cx, deserializer.deserialize_tuple_struct($type_name, $nfields, $visitor_expr)) }; @@ -394,7 +398,7 @@ fn deserialize_seq( .map(|(i, field)| { let name = builder.id(format!("__field{}", i)); if field.attrs.skip_deserializing() { - let default = expr_is_missing(cx, &field.attrs); + let default = expr_is_missing(cx, builder, &field.attrs); quote_stmt!(cx, let $name = $default; ).unwrap() @@ -502,7 +506,7 @@ fn deserialize_struct( impl_generics: &ast::Generics, ty: P, fields: &[item::Field], - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { let where_clause = &impl_generics.where_clause; @@ -535,7 +539,7 @@ fn deserialize_struct( type_path.clone(), impl_generics, fields, - container_attrs, + item_attrs, ); let is_enum = variant_ident.is_some(); @@ -543,7 +547,7 @@ fn deserialize_struct( quote_expr!(cx, visitor.visit_struct(FIELDS, $visitor_expr)) } else { - let type_name = container_attrs.name().deserialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); quote_expr!(cx, deserializer.deserialize_struct($type_name, FIELDS, $visitor_expr)) }; @@ -584,11 +588,11 @@ fn deserialize_item_enum( impl_generics: &ast::Generics, ty: P, variants: &[item::Variant], - container_attrs: &attr::ContainerAttrs + item_attrs: &attr::Item ) -> P { let where_clause = &impl_generics.where_clause; - let type_name = container_attrs.name().deserialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); let variant_visitor = deserialize_field_visitor( cx, @@ -596,7 +600,7 @@ fn deserialize_item_enum( variants.iter() .map(|variant| variant.attrs.name().deserialize_name()) .collect(), - container_attrs, + item_attrs, true, ); @@ -610,7 +614,7 @@ fn deserialize_item_enum( const VARIANTS: &'static [&'static str] = $variants_expr; ).unwrap(); - let ignored_arm = if container_attrs.deny_unknown_fields() { + let ignored_arm = if item_attrs.deny_unknown_fields() { None } else { Some(quote_arm!(cx, __Field::__ignore => { Err(_serde::de::Error::end_of_stream()) })) @@ -630,7 +634,7 @@ fn deserialize_item_enum( impl_generics, ty.clone(), variant, - container_attrs, + item_attrs, ); let arm = quote_arm!(cx, $variant_name => { $expr }); @@ -675,7 +679,7 @@ fn deserialize_variant( generics: &ast::Generics, ty: P, variant: &item::Variant, - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { let variant_ident = variant.ident; @@ -705,7 +709,7 @@ fn deserialize_variant( generics, ty, &variant.fields, - container_attrs, + item_attrs, ) } item::Style::Struct => { @@ -717,7 +721,7 @@ fn deserialize_variant( generics, ty, &variant.fields, - container_attrs, + item_attrs, ) } } @@ -753,7 +757,7 @@ fn deserialize_field_visitor( cx: &ExtCtxt, builder: &aster::AstBuilder, field_names: Vec, - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, is_variant: bool, ) -> Vec> { // Create the field names for the fields. @@ -761,7 +765,7 @@ fn deserialize_field_visitor( .map(|i| builder.id(format!("__field{}", i))) .collect(); - let ignore_variant = if container_attrs.deny_unknown_fields() { + let ignore_variant = if item_attrs.deny_unknown_fields() { None } else { let skip_ident = builder.id("__ignore"); @@ -792,7 +796,7 @@ fn deserialize_field_visitor( (builder.expr().str("expected a field"), builder.id("unknown_field")) }; - let fallthrough_index_arm_expr = if !is_variant && !container_attrs.deny_unknown_fields() { + let fallthrough_index_arm_expr = if !is_variant && !item_attrs.deny_unknown_fields() { quote_expr!(cx, Ok(__Field::__ignore)) } else { quote_expr!(cx, { @@ -819,7 +823,7 @@ fn deserialize_field_visitor( }) .collect(); - let fallthrough_str_arm_expr = if !is_variant && !container_attrs.deny_unknown_fields() { + let fallthrough_str_arm_expr = if !is_variant && !item_attrs.deny_unknown_fields() { quote_expr!(cx, Ok(__Field::__ignore)) } else { quote_expr!(cx, Err(_serde::de::Error::$unknown_ident(value))) @@ -847,7 +851,7 @@ fn deserialize_field_visitor( }) .collect(); - let fallthrough_bytes_arm_expr = if !is_variant && !container_attrs.deny_unknown_fields() { + let fallthrough_bytes_arm_expr = if !is_variant && !item_attrs.deny_unknown_fields() { quote_expr!(cx, Ok(__Field::__ignore)) } else { quote_expr!(cx, { @@ -916,7 +920,7 @@ fn deserialize_struct_visitor( struct_path: ast::Path, impl_generics: &ast::Generics, fields: &[item::Field], - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> (Vec>, ast::Stmt, P) { let field_exprs = fields.iter() .map(|field| field.attrs.name().deserialize_name()) @@ -926,7 +930,7 @@ fn deserialize_struct_visitor( cx, builder, field_exprs, - container_attrs, + item_attrs, false, ); @@ -937,7 +941,7 @@ fn deserialize_struct_visitor( struct_path, impl_generics, fields, - container_attrs, + item_attrs, ); let fields_expr = builder.expr().ref_().slice() @@ -968,7 +972,7 @@ fn deserialize_map( struct_path: ast::Path, impl_generics: &ast::Generics, fields: &[item::Field], - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { // Create the field names for the fields. let fields_names = fields.iter() @@ -1033,7 +1037,7 @@ fn deserialize_map( .collect::>(); // Visit ignored values to consume them - let ignored_arm = if container_attrs.deny_unknown_fields() { + let ignored_arm = if item_attrs.deny_unknown_fields() { None } else { Some(quote_arm!(cx, @@ -1044,7 +1048,7 @@ fn deserialize_map( let extract_values = fields_names.iter() .filter(|&&(field, _)| !field.attrs.skip_deserializing()) .map(|&(field, name)| { - let missing_expr = expr_is_missing(cx, &field.attrs); + let missing_expr = expr_is_missing(cx, builder, &field.attrs); quote_stmt!(cx, let $name = match $name { @@ -1067,7 +1071,7 @@ fn deserialize_map( } }, if field.attrs.skip_deserializing() { - expr_is_missing(cx, &field.attrs) + expr_is_missing(cx, builder, &field.attrs) } else { builder.expr().id(name) } @@ -1149,7 +1153,8 @@ fn wrap_deserialize_with( fn expr_is_missing( cx: &ExtCtxt, - attrs: &attr::FieldAttrs, + builder: &aster::AstBuilder, + attrs: &attr::Field, ) -> P { match *attrs.default() { attr::FieldDefault::Default => { @@ -1161,7 +1166,7 @@ fn expr_is_missing( attr::FieldDefault::None => { /* below */ } } - let name = attrs.name().deserialize_name_expr(); + let name = name_expr(builder, attrs.name()); match attrs.deserialize_with() { None => { quote_expr!(cx, try!(visitor.missing_field($name))) @@ -1173,6 +1178,13 @@ fn expr_is_missing( } } +fn name_expr( + builder: &aster::AstBuilder, + name: &attr::Name, +) -> P { + builder.expr().str(name.deserialize_name()) +} + fn check_no_str( cx: &ExtCtxt, item: &item::Item, diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index a5c51a40..0d69c94a 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -7,6 +7,7 @@ extern crate aster; extern crate quasi; +extern crate serde_item as item; #[cfg(feature = "with-syntex")] extern crate syntex; diff --git a/serde_codegen/src/lib.rs.in b/serde_codegen/src/lib.rs.in index 946bf0f5..0a80f38a 100644 --- a/serde_codegen/src/lib.rs.in +++ b/serde_codegen/src/lib.rs.in @@ -1,6 +1,4 @@ -mod attr; mod bound; mod de; mod error; -mod item; mod ser; diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index de782490..403d077c 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -5,10 +5,8 @@ use syntax::codemap::Span; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ptr::P; -use attr; use bound; -use error::Error; -use item; +use item::{self, attr}; pub fn expand_derive_serialize( cx: &mut ExtCtxt, @@ -27,26 +25,26 @@ pub fn expand_derive_serialize( } }; - let builder = aster::AstBuilder::new().span(span); - - let impl_item = match serialize_item(cx, &builder, &item) { + let item = match item::Item::from_ast(cx, item) { Ok(item) => item, - Err(Error) => { - // An error occured, but it should have been reported already. + Err(item::Error::UnexpectedItemKind) => { + cx.span_err(item.span, + "`#[derive(Serialize)]` may only be applied to structs and enums"); return; } }; + let builder = aster::AstBuilder::new().span(span); + + let impl_item = serialize_item(cx, &builder, &item); push(Annotatable::Item(impl_item)) } fn serialize_item( cx: &ExtCtxt, builder: &aster::AstBuilder, - item: &ast::Item, -) -> Result, Error> { - let item = try!(item::Item::from_ast(cx, "Serialize", item)); - + item: &item::Item, +) -> P { let impl_generics = build_impl_generics(builder, &item); let ty = builder.ty().path() @@ -63,7 +61,7 @@ fn serialize_item( let dummy_const = builder.id(format!("_IMPL_SERIALIZE_FOR_{}", item.ident)); - Ok(quote_item!(cx, + quote_item!(cx, #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] const $dummy_const: () = { extern crate serde as _serde; @@ -76,7 +74,7 @@ fn serialize_item( } } }; - ).unwrap()) + ).unwrap() } // All the generics in the input, plus a bound `T: Serialize` for each generic @@ -107,7 +105,7 @@ fn build_impl_generics( // serialized by us so we do not generate a bound. Fields with a `bound` // attribute specify their own bound so we do not generate one. All other fields // may need a `T: Serialize` bound where T is the type of the field. -fn needs_serialize_bound(attrs: &attr::FieldAttrs) -> bool { +fn needs_serialize_bound(attrs: &attr::Field) -> bool { !attrs.skip_serializing() && attrs.serialize_with().is_none() && attrs.ser_bound().is_none() @@ -169,6 +167,7 @@ fn serialize_body( item::Body::Struct(item::Style::Unit, _) => { serialize_unit_struct( cx, + builder, &item.attrs) } } @@ -176,9 +175,10 @@ fn serialize_body( fn serialize_unit_struct( cx: &ExtCtxt, - container_attrs: &attr::ContainerAttrs, + builder: &aster::AstBuilder, + item_attrs: &attr::Item, ) -> P { - let type_name = container_attrs.name().serialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); quote_expr!(cx, _serializer.serialize_unit_struct($type_name) @@ -189,16 +189,16 @@ fn serialize_newtype_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, impl_generics: &ast::Generics, - container_ty: P, + item_ty: P, field: &item::Field, - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { - let type_name = container_attrs.name().serialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); let mut field_expr = quote_expr!(cx, &self.0); if let Some(path) = field.attrs.serialize_with() { field_expr = wrap_serialize_with(cx, builder, - &container_ty, impl_generics, &field.ty, path, field_expr); + &item_ty, impl_generics, &field.ty, path, field_expr); } quote_expr!(cx, @@ -212,7 +212,7 @@ fn serialize_tuple_struct( impl_generics: &ast::Generics, ty: P, fields: &[item::Field], - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( cx, @@ -228,7 +228,7 @@ fn serialize_tuple_struct( false, ); - let type_name = container_attrs.name().serialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); quote_expr!(cx, { $visitor_struct @@ -247,7 +247,7 @@ fn serialize_struct( impl_generics: &ast::Generics, ty: P, fields: &[item::Field], - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { let (visitor_struct, visitor_impl) = serialize_struct_visitor( cx, @@ -263,7 +263,7 @@ fn serialize_struct( false, ); - let type_name = container_attrs.name().serialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); quote_expr!(cx, { $visitor_struct @@ -283,7 +283,7 @@ fn serialize_item_enum( impl_generics: &ast::Generics, ty: P, variants: &[item::Variant], - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { let arms: Vec<_> = variants.iter() @@ -297,7 +297,7 @@ fn serialize_item_enum( ty.clone(), variant, variant_index, - container_attrs, + item_attrs, ) }) .collect(); @@ -317,12 +317,12 @@ fn serialize_variant( ty: P, variant: &item::Variant, variant_index: usize, - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> ast::Arm { - let type_name = container_attrs.name().serialize_name_expr(); + let type_name = name_expr(builder, item_attrs.name()); let variant_ident = variant.ident; - let variant_name = variant.attrs.name().serialize_name_expr(); + let variant_name = name_expr(builder, variant.attrs.name()); match variant.style { item::Style::Unit => { @@ -414,7 +414,7 @@ fn serialize_variant( ty, &variant.fields, field_names, - container_attrs, + item_attrs, ); quote_arm!(cx, @@ -430,14 +430,14 @@ fn serialize_newtype_variant( type_name: P, variant_index: usize, variant_name: P, - container_ty: P, + item_ty: P, generics: &ast::Generics, field: &item::Field, ) -> P { let mut field_expr = quote_expr!(cx, __simple_value); if let Some(path) = field.attrs.serialize_with() { field_expr = wrap_serialize_with(cx, builder, - &container_ty, generics, &field.ty, path, field_expr); + &item_ty, generics, &field.ty, path, field_expr); } quote_expr!(cx, @@ -512,7 +512,7 @@ fn serialize_struct_variant( ty: P, fields: &[item::Field], field_names: Vec, - container_attrs: &attr::ContainerAttrs, + item_attrs: &attr::Item, ) -> P { let variant_generics = builder.generics() .with(generics.clone()) @@ -572,14 +572,14 @@ fn serialize_struct_variant( true, ); - let container_name = container_attrs.name().serialize_name_expr(); + let item_name = name_expr(builder, item_attrs.name()); quote_expr!(cx, { $variant_struct $visitor_struct $visitor_impl _serializer.serialize_struct_variant( - $container_name, + $item_name, $variant_index, $variant_name, Visitor { @@ -692,7 +692,7 @@ fn serialize_struct_visitor( field_expr = quote_expr!(cx, &$field_expr); } - let key_expr = field.attrs.name().serialize_name_expr(); + let key_expr = name_expr(builder, field.attrs.name()); let continue_if_skip = field.attrs.skip_serializing_if() .map(|path| quote_stmt!(cx, if $path($field_expr) { continue })); @@ -781,7 +781,7 @@ fn serialize_struct_visitor( fn wrap_serialize_with( cx: &ExtCtxt, builder: &aster::AstBuilder, - container_ty: &P, + item_ty: &P, generics: &ast::Generics, field_ty: &P, path: &ast::Path, @@ -803,7 +803,7 @@ fn wrap_serialize_with( quote_expr!(cx, { struct __SerializeWith $wrapper_generics $where_clause { value: &'__a $field_ty, - phantom: ::std::marker::PhantomData<$container_ty>, + phantom: ::std::marker::PhantomData<$item_ty>, } impl $wrapper_generics _serde::ser::Serialize for $wrapper_ty $where_clause { @@ -816,7 +816,14 @@ fn wrap_serialize_with( __SerializeWith { value: $value, - phantom: ::std::marker::PhantomData::<$container_ty>, + phantom: ::std::marker::PhantomData::<$item_ty>, } }) } + +fn name_expr( + builder: &aster::AstBuilder, + name: &attr::Name, +) -> P { + builder.expr().str(name.serialize_name()) +} diff --git a/serde_item/Cargo.toml b/serde_item/Cargo.toml new file mode 100644 index 00000000..3f88d65d --- /dev/null +++ b/serde_item/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "serde_item" +version = "0.1.0" +authors = ["Erick Tryzelaar "] +license = "MIT/Apache-2.0" +description = "AST representation used by Serde codegen. Unstable." +repository = "https://github.com/serde-rs/serde" +documentation = "https://github.com/serde-rs/serde" +keywords = ["serde", "serialization"] +include = ["Cargo.toml", "src/**/*.rs"] + +[features] +default = ["with-syntex"] +nightly-testing = ["clippy"] +with-syntex = ["syntex_syntax"] + +[dependencies] +clippy = { version = "^0.*", optional = true } +syntex_syntax = { version = "^0.35.0", optional = true } diff --git a/serde_codegen/src/attr.rs b/serde_item/src/attr.rs similarity index 93% rename from serde_codegen/src/attr.rs rename to serde_item/src/attr.rs index 4ee45270..8a91914d 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_item/src/attr.rs @@ -1,4 +1,5 @@ use std::rc::Rc; + use syntax::ast::{self, TokenTree}; use syntax::attr; use syntax::codemap::{Span, Spanned, respan}; @@ -10,14 +11,9 @@ use syntax::parse; use syntax::print::pprust::{lit_to_string, meta_item_to_string}; use syntax::ptr::P; -use aster::AstBuilder; -use aster::ident::ToIdent; - -use error::Error; - // This module handles parsing of `#[serde(...)]` attributes. The entrypoints -// are `ContainerAttrs::from_item`, `VariantAttrs::from_variant`, and -// `FieldAttrs::from_field`. Each returns an instance of the corresponding +// are `attr::Item::from_ast`, `attr::Variant::from_ast`, and +// `attr::Field::from_ast`. Each returns an instance of the corresponding // struct. Note that none of them return a Result. Unrecognized, malformed, or // duplicated attributes result in a span_err but otherwise are ignored. The // user will see errors simultaneously for all bad attributes in the crate @@ -101,11 +97,6 @@ impl Name { } } - /// Return the container name expression for the container when deserializing. - pub fn serialize_name_expr(&self) -> P { - AstBuilder::new().expr().str(self.serialize_name()) - } - /// Return the container name for the container when deserializing. pub fn deserialize_name(&self) -> InternedString { match self.deserialize_name { @@ -113,25 +104,20 @@ impl Name { None => self.ident.name.as_str(), } } - - /// Return the container name expression for the container when deserializing. - pub fn deserialize_name_expr(&self) -> P { - AstBuilder::new().expr().str(self.deserialize_name()) - } } /// Represents container (e.g. struct) attribute information #[derive(Debug)] -pub struct ContainerAttrs { +pub struct Item { name: Name, deny_unknown_fields: bool, ser_bound: Option>, de_bound: Option>, } -impl ContainerAttrs { +impl Item { /// Extract out the `#[serde(...)]` attributes from an item. - pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Self { + pub fn from_ast(cx: &ExtCtxt, item: &ast::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"); @@ -189,7 +175,7 @@ impl ContainerAttrs { } } - ContainerAttrs { + Item { name: Name { ident: item.ident, serialize_name: ser_name.get(), @@ -220,12 +206,12 @@ impl ContainerAttrs { /// Represents variant attribute information #[derive(Debug)] -pub struct VariantAttrs { +pub struct Variant { name: Name, } -impl VariantAttrs { - pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Self { +impl Variant { + pub fn from_ast(cx: &ExtCtxt, variant: &ast::Variant) -> Self { let mut ser_name = Attr::none(cx, "rename"); let mut de_name = Attr::none(cx, "rename"); @@ -259,7 +245,7 @@ impl VariantAttrs { } } - VariantAttrs { + Variant { name: Name { ident: variant.node.name, serialize_name: ser_name.get(), @@ -275,7 +261,7 @@ impl VariantAttrs { /// Represents field attribute information #[derive(Debug)] -pub struct FieldAttrs { +pub struct Field { name: Name, skip_serializing: bool, skip_deserializing: bool, @@ -298,11 +284,11 @@ pub enum FieldDefault { Path(ast::Path), } -impl FieldAttrs { +impl Field { /// Extract out the `#[serde(...)]` attributes from a struct field. - pub fn from_field(cx: &ExtCtxt, - index: usize, - field: &ast::StructField) -> Self { + pub fn from_ast(cx: &ExtCtxt, + index: usize, + field: &ast::StructField) -> 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"); @@ -316,7 +302,7 @@ impl FieldAttrs { let field_ident = match field.ident { Some(ident) => ident, - None => index.to_string().to_ident(), + None => ast::Ident::with_empty_ctxt(token::intern(&index.to_string())), }; for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) { @@ -414,7 +400,7 @@ impl FieldAttrs { default.set_if_none(span, FieldDefault::Default); } - FieldAttrs { + Field { name: Name { ident: field_ident, serialize_name: ser_name.get(), @@ -473,8 +459,8 @@ fn get_ser_and_de( attribute: &'static str, items: &[P], f: F -) -> Result<(Option>, Option>), Error> - where F: Fn(&ExtCtxt, &str, &ast::Lit) -> Result, +) -> Result<(Option>, Option>), ()> + where F: Fn(&ExtCtxt, &str, &ast::Lit) -> Result, { let mut ser_item = Attr::none(cx, attribute); let mut de_item = Attr::none(cx, attribute); @@ -500,7 +486,7 @@ fn get_ser_and_de( attribute, meta_item_to_string(item))); - return Err(Error); + return Err(()); } } } @@ -511,14 +497,14 @@ fn get_ser_and_de( fn get_renames( cx: &ExtCtxt, items: &[P], -) -> Result<(Option>, Option>), Error> { +) -> Result<(Option>, Option>), ()> { get_ser_and_de(cx, "rename", items, get_str_from_lit) } fn get_where_predicates( cx: &ExtCtxt, items: &[P], -) -> Result<(Option>>, Option>>), Error> { +) -> Result<(Option>>, Option>>), ()> { get_ser_and_de(cx, "bound", items, parse_lit_into_where) } @@ -579,7 +565,7 @@ impl<'a, 'b> Folder for Respanner<'a, 'b> { } } -fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result { +fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result { match lit.node { ast::LitKind::Str(ref s, _) => Ok(s.clone()), _ => { @@ -589,7 +575,7 @@ fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result Result(cx: &ExtCtxt, name: &str, string: String, action: F) -> Result +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( @@ -618,7 +604,7 @@ fn parse_string_via_tts(cx: &ExtCtxt, name: &str, string: String, action: Ok(path) => path, Err(mut e) => { e.emit(); - return Err(Error); + return Err(()); } }; @@ -627,14 +613,14 @@ fn parse_string_via_tts(cx: &ExtCtxt, name: &str, string: String, action: Ok(()) => { } Err(mut e) => { e.emit(); - return Err(Error); + return Err(()); } } Ok(path) } -fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result { +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| { @@ -642,7 +628,7 @@ fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result Result, Error> { +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() { return Ok(Vec::new()); diff --git a/serde_item/src/error.rs b/serde_item/src/error.rs new file mode 100644 index 00000000..08337c0b --- /dev/null +++ b/serde_item/src/error.rs @@ -0,0 +1,19 @@ +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/src/item.rs b/serde_item/src/lib.rs similarity index 80% rename from serde_codegen/src/item.rs rename to serde_item/src/lib.rs index e4a76d3a..fc8afe60 100644 --- a/serde_codegen/src/item.rs +++ b/serde_item/src/lib.rs @@ -1,15 +1,29 @@ +#![cfg_attr(feature = "nightly-testing", plugin(clippy))] +#![cfg_attr(feature = "nightly-testing", 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(not(feature = "with-syntex"))] +#[macro_use] +extern crate syntax; + use syntax::ast; use syntax::codemap; use syntax::ext::base::ExtCtxt; use syntax::ptr::P; -use attr; -use error::Error; +pub mod attr; + +mod error; +pub use error::Error; pub struct Item<'a> { pub ident: ast::Ident, pub span: codemap::Span, - pub attrs: attr::ContainerAttrs, + pub attrs: attr::Item, pub body: Body<'a>, pub generics: &'a ast::Generics, } @@ -21,7 +35,8 @@ pub enum Body<'a> { pub struct Variant<'a> { pub ident: ast::Ident, - pub attrs: attr::VariantAttrs, + pub span: codemap::Span, + pub attrs: attr::Variant, pub style: Style, pub fields: Vec>, } @@ -29,7 +44,7 @@ pub struct Variant<'a> { pub struct Field<'a> { pub ident: Option, pub span: codemap::Span, - pub attrs: attr::FieldAttrs, + pub attrs: attr::Field, pub ty: &'a P, } @@ -43,10 +58,9 @@ pub enum Style { impl<'a> Item<'a> { pub fn from_ast( cx: &ExtCtxt, - derive_trait: &'static str, item: &'a ast::Item, ) -> Result, Error> { - let attrs = attr::ContainerAttrs::from_item(cx, item); + let attrs = attr::Item::from_ast(cx, item); let (body, generics) = match item.node { ast::ItemKind::Enum(ref enum_def, ref generics) => { @@ -58,10 +72,7 @@ impl<'a> Item<'a> { (Body::Struct(style, fields), generics) } _ => { - cx.span_err(item.span, &format!( - "`#[derive({})]` may only be applied to structs and enums", - derive_trait)); - return Err(Error); + return Err(Error::UnexpectedItemKind); } }; @@ -75,6 +86,20 @@ impl<'a> Item<'a> { } } +impl<'a> Body<'a> { + pub fn all_fields(&'a self) -> Box> + 'a> { + match *self { + Body::Enum(ref variants) => { + Box::new(variants.iter() + .flat_map(|variant| variant.fields.iter())) + } + Body::Struct(_, ref fields) => { + Box::new(fields.iter()) + } + } + } +} + fn enum_from_ast<'a>( cx: &ExtCtxt, enum_def: &'a ast::EnumDef, @@ -84,7 +109,8 @@ fn enum_from_ast<'a>( let (style, fields) = struct_from_ast(cx, &variant.node.data); Variant { ident: variant.node.name, - attrs: attr::VariantAttrs::from_variant(cx, variant), + span: variant.span, + attrs: attr::Variant::from_ast(cx, variant), style: style, fields: fields, } @@ -122,23 +148,9 @@ fn fields_from_ast<'a>( Field { ident: field.ident, span: field.span, - attrs: attr::FieldAttrs::from_field(cx, i, field), + attrs: attr::Field::from_ast(cx, i, field), ty: &field.ty, } }) .collect() } - -impl<'a> Body<'a> { - pub fn all_fields(&'a self) -> Box> + 'a> { - match *self { - Body::Enum(ref variants) => { - Box::new(variants.iter() - .flat_map(|variant| variant.fields.iter())) - } - Body::Struct(_, ref fields) => { - Box::new(fields.iter()) - } - } - } -}