From 8df841f04861d8429f7ef0eb8444638ce77db787 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Mon, 18 Jan 2016 12:39:46 -0800 Subject: [PATCH] fix(errors): Report errors on unknown #[serde(...)] attributes Closes #51, #175, and #187 --- serde_codegen/src/attr.rs | 104 ++++++-- serde_codegen/src/de.rs | 230 +++++++++--------- serde_codegen/src/field.rs | 29 ++- serde_codegen/src/lib.rs | 5 + serde_codegen/src/ser.rs | 140 ++++++----- serde_macros/Cargo.toml | 1 + .../compile-fail/reject-unknown-attributes.rs | 30 +++ serde_macros/tests/compile_tests.rs | 25 ++ serde_macros/tests/test.rs | 2 + serde_tests/tests/token.rs | 1 - 10 files changed, 365 insertions(+), 202 deletions(-) create mode 100644 serde_macros/tests/compile-fail/reject-unknown-attributes.rs create mode 100644 serde_macros/tests/compile_tests.rs diff --git a/serde_codegen/src/attr.rs b/serde_codegen/src/attr.rs index c4425dda..be54f683 100644 --- a/serde_codegen/src/attr.rs +++ b/serde_codegen/src/attr.rs @@ -4,6 +4,7 @@ use std::collections::HashSet; use syntax::ast; use syntax::attr; use syntax::ext::base::ExtCtxt; +use syntax::print::pprust::meta_item_to_string; use syntax::ptr::P; use aster; @@ -104,6 +105,7 @@ impl FieldAttrs { } pub struct FieldAttrsBuilder<'a> { + cx: &'a ExtCtxt<'a>, builder: &'a aster::AstBuilder, skip_serializing_field: bool, skip_serializing_field_if_empty: bool, @@ -114,8 +116,10 @@ pub struct FieldAttrsBuilder<'a> { } impl<'a> FieldAttrsBuilder<'a> { - pub fn new(builder: &'a aster::AstBuilder) -> FieldAttrsBuilder<'a> { + pub fn new(cx: &'a ExtCtxt<'a>, + builder: &'a aster::AstBuilder) -> FieldAttrsBuilder<'a> { FieldAttrsBuilder { + cx: cx, builder: builder, skip_serializing_field: false, skip_serializing_field_if_empty: false, @@ -126,7 +130,7 @@ impl<'a> FieldAttrsBuilder<'a> { } } - pub fn field(mut self, field: &ast::StructField) -> FieldAttrsBuilder<'a> { + pub fn field(mut self, field: &ast::StructField) -> Result, ()> { match field.node.kind { ast::NamedField(name, _) => { self.name = Some(self.builder.expr().str(name)); @@ -137,28 +141,36 @@ impl<'a> FieldAttrsBuilder<'a> { self.attrs(&field.node.attrs) } - pub fn attrs(self, attrs: &[ast::Attribute]) -> FieldAttrsBuilder<'a> { - attrs.iter().fold(self, FieldAttrsBuilder::attr) + pub fn attrs(mut self, attrs: &[ast::Attribute]) -> Result, ()> { + for attr in attrs { + self = try!(self.attr(attr)); + } + + Ok(self) } - pub fn attr(self, attr: &ast::Attribute) -> FieldAttrsBuilder<'a> { + pub fn attr(mut self, attr: &ast::Attribute) -> Result, ()> { match attr.node.value.node { ast::MetaList(ref name, ref items) if name == &"serde" => { attr::mark_used(&attr); - items.iter().fold(self, FieldAttrsBuilder::meta_item) + for item in items { + self = try!(self.meta_item(item)); + } + + Ok(self) } _ => { - self + Ok(self) } } } - pub fn meta_item(mut self, meta_item: &P) -> FieldAttrsBuilder<'a> { + pub fn meta_item(mut self, meta_item: &P) -> Result, ()> { match meta_item.node { ast::MetaNameValue(ref name, ref lit) if name == &"rename" => { let expr = self.builder.expr().build_lit(P(lit.clone())); - self.name(expr) + Ok(self.name(expr)) } ast::MetaList(ref name, ref items) if name == &"rename" => { for item in items { @@ -172,23 +184,27 @@ impl<'a> FieldAttrsBuilder<'a> { _ => { } } } - self + + Ok(self) } ast::MetaWord(ref name) if name == &"default" => { - self.default() + Ok(self.default()) } ast::MetaWord(ref name) if name == &"skip_serializing" => { - self.skip_serializing_field() + Ok(self.skip_serializing_field()) } ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => { - self.skip_serializing_field_if_empty() + Ok(self.skip_serializing_field_if_empty()) } ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => { - self.skip_serializing_field_if_none() + Ok(self.skip_serializing_field_if_none()) } _ => { - // Ignore unknown meta variables for now. - self + self.cx.span_err( + meta_item.span, + &format!("unknown serde field attribute `{}`", + meta_item_to_string(meta_item))); + Err(()) } } } @@ -243,3 +259,59 @@ impl<'a> FieldAttrsBuilder<'a> { } } } + +/// Represents container (e.g. struct) attribute information +#[derive(Debug)] +pub struct ContainerAttrs; + +pub struct ContainerAttrsBuilder<'a> { + cx: &'a ExtCtxt<'a>, +} + +impl<'a> ContainerAttrsBuilder<'a> { + pub fn new(cx: &'a ExtCtxt) -> Self { + ContainerAttrsBuilder { + cx: cx, + } + } + + pub fn attrs(mut self, attrs: &[ast::Attribute]) -> Result { + for attr in attrs { + self = try!(self.attr(attr)); + } + + Ok(self) + } + + pub fn attr(mut self, attr: &ast::Attribute) -> Result { + match attr.node.value.node { + ast::MetaList(ref name, ref items) if name == &"serde" => { + attr::mark_used(&attr); + for item in items { + self = try!(self.meta_item(item)); + } + + Ok(self) + } + _ => { + Ok(self) + } + } + } + + pub fn meta_item(self, meta_item: &P) -> Result { + match meta_item.node { + _ => { + self.cx.span_err( + meta_item.span, + &format!("unknown serde container attribute `{}`", + meta_item_to_string(meta_item))); + Err(()) + } + } + } + + pub fn build(self) -> ContainerAttrs { + ContainerAttrs + } +} diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index 235b7d47..6980507b 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -57,13 +57,13 @@ pub fn expand_derive_deserialize( .segment(item.ident).with_generics(impl_generics.clone()).build() .build(); - let body = deserialize_body( - cx, - &builder, - &item, - &impl_generics, - ty.clone(), - ); + let body = match deserialize_body(cx, &builder, &item, &impl_generics, ty.clone()) { + Ok(body) => body, + Err(()) => { + // An error occured, but it should have been reported already. + return; + } + }; let where_clause = &impl_generics.where_clause; @@ -86,7 +86,11 @@ fn deserialize_body( item: &Item, impl_generics: &ast::Generics, ty: P, -) -> P { +) -> Result, ()> { + // Note: While we don't have any container attributes, we still want to try to + // parse them so we can report a proper error if we get passed an unknown attribute. + let _ = try!(field::container_attrs(cx, item)); + match item.node { ast::ItemStruct(ref variant_data, _) => { deserialize_item_struct( @@ -124,7 +128,7 @@ fn deserialize_item_struct( ty: P, span: Span, variant_data: &ast::VariantData, -) -> P { +) -> Result, ()> { match *variant_data { ast::VariantData::Unit(_) => { deserialize_unit_struct( @@ -180,14 +184,14 @@ fn deserialize_visitor( trait_generics: &ast::Generics, forward_ty_params: Vec, forward_tys: Vec> -) -> (P, P, P, ast::Generics) { +) -> Result<(P, P, P, ast::Generics), ()> { if trait_generics.ty_params.is_empty() && forward_tys.is_empty() { - ( + Ok(( builder.item().tuple_struct("__Visitor").build(), builder.ty().id("__Visitor"), builder.expr().id("__Visitor"), trait_generics.clone(), - ) + )) } else { let placeholders : Vec<_> = trait_generics.ty_params.iter() .map(|t| builder.ty().id(t.ident)) @@ -197,7 +201,7 @@ fn deserialize_visitor( ty_params.extend(trait_generics.ty_params.into_vec()); trait_generics.ty_params = P::from_vec(ty_params); - ( + Ok(( builder.item().tuple_struct("__Visitor") .generics().with(trait_generics.clone()).build() .with_tys({ @@ -235,7 +239,7 @@ fn deserialize_visitor( }) .build(), trait_generics, - ) + )) } } @@ -259,10 +263,10 @@ fn deserialize_unit_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, type_ident: Ident, -) -> P { +) -> Result, ()> { let type_name = builder.expr().str(type_ident); - quote_expr!(cx, { + Ok(quote_expr!(cx, { struct __Visitor; impl ::serde::de::Visitor for __Visitor { @@ -285,7 +289,7 @@ fn deserialize_unit_struct( } deserializer.visit_unit_struct($type_name, __Visitor) - }) + })) } fn deserialize_newtype_struct( @@ -294,16 +298,15 @@ fn deserialize_newtype_struct( type_ident: Ident, impl_generics: &ast::Generics, ty: P, -) -> P { +) -> Result, ()> { let where_clause = &impl_generics.where_clause; - let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = - deserialize_visitor( - builder, - impl_generics, - vec![deserializer_ty_param(builder)], - vec![deserializer_ty_arg(builder)], - ); + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor( + builder, + impl_generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + )); let visit_seq_expr = deserialize_seq( cx, @@ -314,7 +317,7 @@ fn deserialize_newtype_struct( let type_name = builder.expr().str(type_ident); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $visitor_item impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause { @@ -337,7 +340,7 @@ fn deserialize_newtype_struct( } deserializer.visit_newtype_struct($type_name, $visitor_expr) - }) + })) } fn deserialize_tuple_struct( @@ -347,16 +350,15 @@ fn deserialize_tuple_struct( impl_generics: &ast::Generics, ty: P, fields: usize, -) -> P { +) -> Result, ()> { let where_clause = &impl_generics.where_clause; - let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = - deserialize_visitor( - builder, - impl_generics, - vec![deserializer_ty_param(builder)], - vec![deserializer_ty_arg(builder)], - ); + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor( + builder, + impl_generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + )); let visit_seq_expr = deserialize_seq( cx, @@ -367,7 +369,7 @@ fn deserialize_tuple_struct( let type_name = builder.expr().str(type_ident); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $visitor_item impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause { @@ -382,7 +384,7 @@ fn deserialize_tuple_struct( } deserializer.visit_tuple_struct($type_name, $fields, $visitor_expr) - }) + })) } fn deserialize_seq( @@ -424,7 +426,7 @@ fn deserialize_struct_as_seq( builder: &aster::AstBuilder, struct_path: ast::Path, fields: &[ast::StructField], -) -> P { +) -> Result, ()> { let let_values: Vec> = (0 .. fields.len()) .map(|i| { let name = builder.id(format!("__field{}", i)); @@ -457,13 +459,13 @@ fn deserialize_struct_as_seq( ) .build(); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $let_values try!(visitor.end()); Ok($result) - }) + })) } fn deserialize_struct( @@ -473,35 +475,35 @@ fn deserialize_struct( impl_generics: &ast::Generics, ty: P, fields: &[ast::StructField], -) -> P { +) -> Result, ()> { let where_clause = &impl_generics.where_clause; - let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = deserialize_visitor( + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor( builder, &impl_generics, vec![deserializer_ty_param(builder)], vec![deserializer_ty_arg(builder)], - ); + )); let type_path = builder.path().id(type_ident).build(); - let visit_seq_expr = deserialize_struct_as_seq( + let visit_seq_expr = try!(deserialize_struct_as_seq( cx, builder, type_path.clone(), fields, - ); + )); - let (field_visitor, fields_stmt, visit_map_expr) = deserialize_struct_visitor( + let (field_visitor, fields_stmt, visit_map_expr) = try!(deserialize_struct_visitor( cx, builder, type_path.clone(), fields, - ); + )); let type_name = builder.expr().str(type_ident); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $field_visitor $visitor_item @@ -527,7 +529,7 @@ fn deserialize_struct( $fields_stmt deserializer.visit_struct($type_name, FIELDS, $visitor_expr) - }) + })) } fn deserialize_item_enum( @@ -537,7 +539,7 @@ fn deserialize_item_enum( impl_generics: &ast::Generics, ty: P, enum_def: &EnumDef, -) -> P { +) -> Result, ()> { let where_clause = &impl_generics.where_clause; let type_name = builder.expr().str(type_ident); @@ -548,7 +550,7 @@ fn deserialize_item_enum( enum_def.variants.iter() .map(|variant| { let expr = builder.expr().str(variant.node.name); - attr::FieldAttrsBuilder::new(builder) + attr::FieldAttrsBuilder::new(cx, builder) .name(expr) .default() .build() @@ -570,35 +572,33 @@ fn deserialize_item_enum( ).unwrap(); // Match arms to extract a variant from a string - let variant_arms: Vec<_> = enum_def.variants.iter() - .enumerate() - .map(|(i, variant)| { - let variant_name = builder.pat().enum_() - .id("__Field").id(format!("__field{}", i)).build() - .build(); + let mut variant_arms = vec![]; + for (i, variant) in enum_def.variants.iter().enumerate() { + let variant_name = builder.pat().enum_() + .id("__Field").id(format!("__field{}", i)).build() + .build(); - let expr = deserialize_variant( - cx, - builder, - type_ident, - impl_generics, - ty.clone(), - variant, - ); - - quote_arm!(cx, $variant_name => { $expr }) - }) - .collect(); - - let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = - deserialize_visitor( + let expr = try!(deserialize_variant( + cx, builder, + type_ident, impl_generics, - vec![deserializer_ty_param(builder)], - vec![deserializer_ty_arg(builder)], - ); + ty.clone(), + variant, + )); - quote_expr!(cx, { + let arm = quote_arm!(cx, $variant_name => { $expr }); + variant_arms.push(arm); + } + + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor( + builder, + impl_generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + )); + + Ok(quote_expr!(cx, { $variant_visitor $visitor_item @@ -618,7 +618,7 @@ fn deserialize_item_enum( $variants_stmt deserializer.visit_enum($type_name, VARIANTS, $visitor_expr) - }) + })) } fn deserialize_variant( @@ -628,21 +628,21 @@ fn deserialize_variant( generics: &ast::Generics, ty: P, variant: &ast::Variant, -) -> P { +) -> Result, ()> { let variant_ident = variant.node.name; match variant.node.data { ast::VariantData::Unit(_) => { - quote_expr!(cx, { + Ok(quote_expr!(cx, { try!(visitor.visit_unit()); Ok($type_ident::$variant_ident) - }) + })) } ast::VariantData::Tuple(ref args, _) if args.len() == 1 => { - quote_expr!(cx, { + Ok(quote_expr!(cx, { let val = try!(visitor.visit_newtype()); Ok($type_ident::$variant_ident(val)) - }) + })) } ast::VariantData::Tuple(ref fields, _) => { deserialize_tuple_variant( @@ -677,16 +677,15 @@ fn deserialize_tuple_variant( generics: &ast::Generics, ty: P, fields: usize, -) -> P { +) -> Result, ()> { let where_clause = &generics.where_clause; - let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = - deserialize_visitor( - builder, - generics, - vec![deserializer_ty_param(builder)], - vec![deserializer_ty_arg(builder)], - ); + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor( + builder, + generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + )); let visit_seq_expr = deserialize_seq( cx, @@ -695,7 +694,7 @@ fn deserialize_tuple_variant( fields, ); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $visitor_item impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause { @@ -709,7 +708,7 @@ fn deserialize_tuple_variant( } visitor.visit_tuple($fields, $visitor_expr) - }) + })) } fn deserialize_struct_variant( @@ -720,7 +719,7 @@ fn deserialize_struct_variant( generics: &ast::Generics, ty: P, fields: &[ast::StructField], -) -> P { +) -> Result, ()> { let where_clause = &generics.where_clause; let type_path = builder.path() @@ -728,29 +727,28 @@ fn deserialize_struct_variant( .id(variant_ident) .build(); - let visit_seq_expr = deserialize_struct_as_seq( + let visit_seq_expr = try!(deserialize_struct_as_seq( cx, builder, type_path.clone(), fields, - ); + )); - let (field_visitor, fields_stmt, field_expr) = deserialize_struct_visitor( + let (field_visitor, fields_stmt, field_expr) = try!(deserialize_struct_visitor( cx, builder, type_path, fields, - ); + )); - let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = - deserialize_visitor( - builder, - generics, - vec![deserializer_ty_param(builder)], - vec![deserializer_ty_arg(builder)], - ); + let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor( + builder, + generics, + vec![deserializer_ty_param(builder)], + vec![deserializer_ty_arg(builder)], + )); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $field_visitor $visitor_item @@ -776,7 +774,7 @@ fn deserialize_struct_variant( $fields_stmt visitor.visit_struct(FIELDS, $visitor_expr) - }) + })) } fn deserialize_field_visitor( @@ -932,19 +930,19 @@ fn deserialize_struct_visitor( builder: &aster::AstBuilder, struct_path: ast::Path, fields: &[ast::StructField], -) -> (Vec>, P, P) { +) -> Result<(Vec>, P, P), ()> { let field_visitor = deserialize_field_visitor( cx, builder, - field::struct_field_attrs(cx, builder, fields), + try!(field::struct_field_attrs(cx, builder, fields)), ); - let visit_map_expr = deserialize_map( + let visit_map_expr = try!(deserialize_map( cx, builder, struct_path, fields, - ); + )); let fields_expr = builder.expr().addr_of().slice() .with_exprs( @@ -964,7 +962,7 @@ fn deserialize_struct_visitor( const FIELDS: &'static [&'static str] = $fields_expr; ).unwrap(); - (field_visitor, fields_stmt, visit_map_expr) + Ok((field_visitor, fields_stmt, visit_map_expr)) } fn deserialize_map( @@ -972,7 +970,7 @@ fn deserialize_map( builder: &aster::AstBuilder, struct_path: ast::Path, fields: &[ast::StructField], -) -> P { +) -> Result, ()> { // Create the field names for the fields. let field_names: Vec = (0 .. fields.len()) .map(|i| builder.id(format!("__field{}", i))) @@ -994,8 +992,10 @@ fn deserialize_map( }) .collect(); + let field_attrs = try!(field::struct_field_attrs(cx, builder, fields)); + let extract_values: Vec> = field_names.iter() - .zip(field::struct_field_attrs(cx, builder, fields).iter()) + .zip(field_attrs.iter()) .map(|(field_name, field_attr)| { let missing_expr = if field_attr.use_default() { quote_expr!(cx, ::std::default::Default::default()) @@ -1048,7 +1048,7 @@ fn deserialize_map( ) .build(); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $let_values while let Some(key) = try!(visitor.visit_key()) { @@ -1062,5 +1062,5 @@ fn deserialize_map( try!(visitor.end()); Ok($result) - }) + })) } diff --git a/serde_codegen/src/field.rs b/serde_codegen/src/field.rs index bf1a522b..43fe4f90 100644 --- a/serde_codegen/src/field.rs +++ b/serde_codegen/src/field.rs @@ -2,16 +2,29 @@ use syntax::ast; use syntax::ext::base::ExtCtxt; use aster; -use attr::{FieldAttrs, FieldAttrsBuilder}; +use attr; pub fn struct_field_attrs( - _cx: &ExtCtxt, + cx: &ExtCtxt, builder: &aster::AstBuilder, fields: &[ast::StructField], -) -> Vec { - fields.iter() - .map(|field| { - FieldAttrsBuilder::new(builder).field(field).build() - }) - .collect() +) -> Result, ()> { + let mut attrs = vec![]; + for field in fields { + let builder = attr::FieldAttrsBuilder::new(cx, builder); + let builder = try!(builder.field(field)); + let attr = builder.build(); + attrs.push(attr); + } + + Ok(attrs) +} + +pub fn container_attrs( + cx: &ExtCtxt, + container: &ast::Item, +) -> Result { + let builder = attr::ContainerAttrsBuilder::new(cx); + let builder = try!(builder.attrs(container.attrs())); + Ok(builder.build()) } diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index 2528e43c..83e768a6 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -18,6 +18,9 @@ extern crate syntax; #[cfg(not(feature = "with-syntex"))] extern crate rustc_plugin; +#[cfg(not(feature = "with-syntex"))] +use syntax::feature_gate::AttributeType; + #[cfg(feature = "with-syntex")] include!(concat!(env!("OUT_DIR"), "/lib.rs")); @@ -72,4 +75,6 @@ pub fn register(reg: &mut rustc_plugin::Registry) { syntax::parse::token::intern("derive_Deserialize"), syntax::ext::base::MultiDecorator( Box::new(de::expand_derive_deserialize))); + + reg.register_attribute("serde".to_owned(), AttributeType::Normal); } diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index 824a3b23..241e4dcd 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -11,7 +11,7 @@ use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::ptr::P; -use field::struct_field_attrs; +use field; pub fn expand_derive_serialize( cx: &mut ExtCtxt, @@ -53,13 +53,13 @@ pub fn expand_derive_serialize( .segment(item.ident).with_generics(impl_generics.clone()).build() .build(); - let body = serialize_body( - cx, - &builder, - &item, - &impl_generics, - ty.clone(), - ); + let body = match serialize_body(cx, &builder, &item, &impl_generics, ty.clone()) { + Ok(body) => body, + Err(()) => { + // An error occured, but it should have been reported already. + return; + } + }; let where_clause = &impl_generics.where_clause; @@ -82,7 +82,11 @@ fn serialize_body( item: &Item, impl_generics: &ast::Generics, ty: P, -) -> P { +) -> Result, ()> { + // Note: While we don't have any container attributes, we still want to try to + // parse them so we can report a proper error if we get passed an unknown attribute. + let _ = try!(field::container_attrs(cx, item)); + match item.node { ast::ItemStruct(ref variant_data, _) => { serialize_item_struct( @@ -107,7 +111,7 @@ fn serialize_body( } _ => { cx.span_bug(item.span, - "expected ItemStruct or ItemEnum in #[derive(Serialize)]") + "expected ItemStruct or ItemEnum in #[derive(Serialize)]"); } } } @@ -120,7 +124,7 @@ fn serialize_item_struct( ty: P, span: Span, variant_data: &ast::VariantData, -) -> P { +) -> Result, ()> { match *variant_data { ast::VariantData::Unit(_) => { serialize_unit_struct( @@ -171,20 +175,24 @@ fn serialize_unit_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, type_ident: Ident -) -> P { +) -> Result, ()> { let type_name = builder.expr().str(type_ident); - quote_expr!(cx, serializer.visit_unit_struct($type_name)) + Ok(quote_expr!(cx, + serializer.visit_unit_struct($type_name) + )) } fn serialize_newtype_struct( cx: &ExtCtxt, builder: &aster::AstBuilder, type_ident: Ident -) -> P { +) -> Result, ()> { let type_name = builder.expr().str(type_ident); - quote_expr!(cx, serializer.visit_newtype_struct($type_name, &self.0)) + Ok(quote_expr!(cx, + serializer.visit_newtype_struct($type_name, &self.0) + )) } fn serialize_tuple_struct( @@ -194,7 +202,7 @@ fn serialize_tuple_struct( impl_generics: &ast::Generics, ty: P, fields: usize, -) -> P { +) -> Result, ()> { let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor( cx, builder, @@ -209,7 +217,7 @@ fn serialize_tuple_struct( let type_name = builder.expr().str(type_ident); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $visitor_struct $visitor_impl serializer.visit_tuple_struct($type_name, Visitor { @@ -217,7 +225,7 @@ fn serialize_tuple_struct( state: 0, _structure_ty: ::std::marker::PhantomData::<&$ty>, }) - }) + })) } fn serialize_struct( @@ -227,8 +235,13 @@ fn serialize_struct( impl_generics: &ast::Generics, ty: P, fields: &[ast::StructField], -) -> P { - let (visitor_struct, visitor_impl) = serialize_struct_visitor( +) -> Result, ()> { + let value_exprs = fields.iter().map(|field| { + let name = field.node.ident().expect("struct has unnamed field"); + quote_expr!(cx, &self.value.$name) + }); + + let (visitor_struct, visitor_impl) = try!(serialize_struct_visitor( cx, builder, ty.clone(), @@ -238,15 +251,12 @@ fn serialize_struct( .build_ty(ty.clone()), fields, impl_generics, - fields.iter().map(|field| { - let name = field.node.ident().expect("struct has unnamed field"); - quote_expr!(cx, &self.value.$name) - }) - ); + value_exprs, + )); let type_name = builder.expr().str(type_ident); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $visitor_struct $visitor_impl serializer.visit_struct($type_name, Visitor { @@ -254,7 +264,7 @@ fn serialize_struct( state: 0, _structure_ty: ::std::marker::PhantomData::<&$ty>, }) - }) + })) } fn serialize_item_enum( @@ -264,27 +274,28 @@ fn serialize_item_enum( impl_generics: &ast::Generics, ty: P, enum_def: &ast::EnumDef, -) -> P { - let arms: Vec = enum_def.variants.iter() - .enumerate() - .map(|(variant_index, variant)| { - serialize_variant( - cx, - builder, - type_ident, - impl_generics, - ty.clone(), - variant, - variant_index, - ) - }) - .collect(); +) -> Result, ()> { + let mut arms = vec![]; - quote_expr!(cx, + for (variant_index, variant) in enum_def.variants.iter().enumerate() { + let arm = try!(serialize_variant( + cx, + builder, + type_ident, + impl_generics, + ty.clone(), + variant, + variant_index, + )); + + arms.push(arm); + } + + Ok(quote_expr!(cx, match *self { $arms } - ) + )) } fn serialize_variant( @@ -295,7 +306,7 @@ fn serialize_variant( ty: P, variant: &ast::Variant, variant_index: usize, -) -> ast::Arm { +) -> Result { let type_name = builder.expr().str(type_ident); let variant_ident = variant.node.name; let variant_name = builder.expr().str(variant_ident); @@ -306,7 +317,7 @@ fn serialize_variant( .id(type_ident).id(variant_ident).build() .build(); - quote_arm!(cx, + Ok(quote_arm!(cx, $pat => { ::serde::ser::Serializer::visit_unit_variant( serializer, @@ -315,7 +326,7 @@ fn serialize_variant( $variant_name, ) } - ) + )) }, ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { let field = builder.id("__simple_value"); @@ -324,7 +335,8 @@ fn serialize_variant( .id(type_ident).id(variant_ident).build() .with_pats(Some(field).into_iter()) .build(); - quote_arm!(cx, + + Ok(quote_arm!(cx, $pat => { ::serde::ser::Serializer::visit_newtype_variant( serializer, @@ -334,7 +346,7 @@ fn serialize_variant( __simple_value, ) } - ) + )) }, ast::VariantData::Tuple(ref fields, _) => { let field_names: Vec = (0 .. fields.len()) @@ -361,7 +373,9 @@ fn serialize_variant( field_names, ); - quote_arm!(cx, $pat => { $expr }) + Ok(quote_arm!(cx, + $pat => { $expr } + )) } ast::VariantData::Struct(ref fields, _) => { let field_names: Vec<_> = (0 .. fields.len()) @@ -386,7 +400,7 @@ fn serialize_variant( ) .build(); - let expr = serialize_struct_variant( + let expr = try!(serialize_struct_variant( cx, builder, type_name, @@ -396,9 +410,11 @@ fn serialize_variant( ty, fields, field_names, - ); + )); - quote_arm!(cx, $pat => { $expr }) + Ok(quote_arm!(cx, + $pat => { $expr } + )) } } } @@ -463,7 +479,7 @@ fn serialize_struct_variant( structure_ty: P, fields: &[ast::StructField], field_names: Vec, -) -> P { +) -> Result, ()> { let value_ty = builder.ty().tuple() .with_tys( fields.iter().map(|field| { @@ -483,7 +499,7 @@ fn serialize_struct_variant( ) .build(); - let (visitor_struct, visitor_impl) = serialize_struct_visitor( + let (visitor_struct, visitor_impl) = try!(serialize_struct_visitor( cx, builder, structure_ty.clone(), @@ -495,9 +511,9 @@ fn serialize_struct_variant( .tup_field(i) .field("value").self_() }) - ); + )); - quote_expr!(cx, { + Ok(quote_expr!(cx, { $visitor_struct $visitor_impl serializer.visit_struct_variant($type_name, $variant_index, $variant_name, Visitor { @@ -505,7 +521,7 @@ fn serialize_struct_variant( state: 0, _structure_ty: ::std::marker::PhantomData::<&$structure_ty>, }) - }) + })) } fn serialize_tuple_struct_visitor( @@ -583,12 +599,12 @@ fn serialize_struct_visitor( fields: &[ast::StructField], generics: &ast::Generics, value_exprs: I, -) -> (P, P) +) -> Result<(P, P), ()> where I: Iterator>, { let value_exprs = value_exprs.collect::>(); - let field_attrs = struct_field_attrs(cx, builder, fields); + let field_attrs = try!(field::struct_field_attrs(cx, builder, fields)); let arms: Vec = field_attrs.iter() .zip(value_exprs.iter()) @@ -651,7 +667,7 @@ fn serialize_struct_visitor( }) .fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr)); - ( + Ok(( quote_item!(cx, struct Visitor $visitor_impl_generics $where_clause { state: usize, @@ -683,5 +699,5 @@ fn serialize_struct_visitor( } } ).unwrap(), - ) + )) } diff --git a/serde_macros/Cargo.toml b/serde_macros/Cargo.toml index ce4ed232..8845c072 100644 --- a/serde_macros/Cargo.toml +++ b/serde_macros/Cargo.toml @@ -17,6 +17,7 @@ clippy = "^0.0.35" serde_codegen = { version = "*", path = "../serde_codegen", default-features = false, features = ["nightly"] } [dev-dependencies] +compiletest_rs = "^0.0.11" num = "^0.1.27" rustc-serialize = "^0.3.16" serde = { version = "*", path = "../serde", features = ["nightly", "num-impls"] } diff --git a/serde_macros/tests/compile-fail/reject-unknown-attributes.rs b/serde_macros/tests/compile-fail/reject-unknown-attributes.rs new file mode 100644 index 00000000..5a4d2776 --- /dev/null +++ b/serde_macros/tests/compile-fail/reject-unknown-attributes.rs @@ -0,0 +1,30 @@ +#![feature(custom_attribute, custom_derive, plugin)] +#![plugin(serde_macros)] + +extern crate serde; + +#[derive(Serialize)] +#[serde(abc="xyz")] //~ unknown serde container attribute `abc = "xyz"` +struct Foo { + x: u32, +} + +#[derive(Deserialize)] +#[serde(abc="xyz")] //~ unknown serde container attribute `abc = "xyz"` +struct Foo { + x: u32, +} + +#[derive(Serialize)] +struct Foo { + #[serde(abc="xyz")] //~ unknown serde field attribute `abc = "xyz"` + x: u32, +} + +#[derive(Deserialize)] +struct Foo { + #[serde(abc="xyz")] //~ unknown serde field attribute `abc = "xyz"` + x: u32, +} + +fn main() { } diff --git a/serde_macros/tests/compile_tests.rs b/serde_macros/tests/compile_tests.rs new file mode 100644 index 00000000..27c212b4 --- /dev/null +++ b/serde_macros/tests/compile_tests.rs @@ -0,0 +1,25 @@ +extern crate compiletest_rs as compiletest; + +use std::path::PathBuf; +use std::env::var; + +fn run_mode(mode: &'static str) { + let mut config = compiletest::default_config(); + + let cfg_mode = mode.parse().ok().expect("Invalid mode"); + + config.target_rustcflags = Some("-L target/debug/ -L target/debug/deps/".to_owned()); + if let Ok(name) = var::<&str>("TESTNAME") { + let s : String = name.to_owned(); + config.filter = Some(s) + } + config.mode = cfg_mode; + config.src_base = PathBuf::from(format!("tests/{}", mode)); + + compiletest::run_tests(&config); +} + +#[test] +fn compile_test() { + run_mode("compile-fail"); +} diff --git a/serde_macros/tests/test.rs b/serde_macros/tests/test.rs index 26ab0042..2f2af797 100644 --- a/serde_macros/tests/test.rs +++ b/serde_macros/tests/test.rs @@ -6,3 +6,5 @@ extern crate serde; extern crate test; include!("../../serde_tests/tests/test.rs.in"); + +mod compile_tests; diff --git a/serde_tests/tests/token.rs b/serde_tests/tests/token.rs index ad86cb67..d0947b14 100644 --- a/serde_tests/tests/token.rs +++ b/serde_tests/tests/token.rs @@ -379,7 +379,6 @@ impl de::Deserializer for Deserializer fn visit(&mut self, mut visitor: V) -> Result where V: de::Visitor, { - println!("visit {:?}", self.tokens.peek()); match self.tokens.next() { Some(Token::Bool(v)) => visitor.visit_bool(v), Some(Token::Isize(v)) => visitor.visit_isize(v),