diff --git a/serde_codegen/src/de.rs b/serde_codegen/src/de.rs index d8a147d0..680c534b 100644 --- a/serde_codegen/src/de.rs +++ b/serde_codegen/src/de.rs @@ -5,11 +5,12 @@ use bound; use internals::ast::{Body, Field, Item, Style, Variant}; use internals::{self, attr}; -pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Tokens { +pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Result { let item = { let ctxt = internals::Ctxt::new(); let item = Item::from_ast(&ctxt, item); check_no_str(&ctxt, &item); + try!(ctxt.check()); item }; @@ -27,7 +28,7 @@ pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Tokens { let dummy_const = aster::id(format!("_IMPL_DESERIALIZE_FOR_{}", item.ident)); - quote! { + Ok(quote! { #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] const #dummy_const: () = { extern crate serde as _serde; @@ -40,7 +41,7 @@ pub fn expand_derive_deserialize(item: &syn::MacroInput) -> Tokens { } } }; - } + }) } // All the generics in the input, plus a bound `T: Deserialize` for each generic diff --git a/serde_codegen/src/lib.rs b/serde_codegen/src/lib.rs index 150360ac..9656016d 100644 --- a/serde_codegen/src/lib.rs +++ b/serde_codegen/src/lib.rs @@ -123,7 +123,7 @@ macro_rules! shim { ($name:ident $pkg:ident :: $func:ident) => { fn $func( cx: &mut ::syntax::ext::base::ExtCtxt, - _span: ::syntax::codemap::Span, + span: ::syntax::codemap::Span, meta_item: &::syntax::ast::MetaItem, annotatable: &::syntax::ext::base::Annotatable, push: &mut FnMut(::syntax::ext::base::Annotatable) @@ -158,7 +158,13 @@ macro_rules! shim { let s = pprust::item_to_string(item); let syn_item = syn::parse_macro_input(&s).unwrap(); - let expanded = $pkg::$func(&syn_item).to_string(); + let expanded = match $pkg::$func(&syn_item) { + Ok(expanded) => expanded.to_string(), + Err(msg) => { + cx.span_err(span, &msg); + return; + } + }; use syntax::parse; let name = stringify!($name).to_string(); @@ -174,21 +180,21 @@ shim!(Serialize ser::expand_derive_serialize); shim!(Deserialize de::expand_derive_deserialize); #[cfg(feature = "with-syn")] -pub fn expand_single_item(item: &str) -> String { +pub fn expand_single_item(item: &str) -> Result { let syn_item = syn::parse_macro_input(item).unwrap(); let (ser, de, syn_item) = strip_serde_derives(syn_item); let expanded_ser = if ser { - Some(ser::expand_derive_serialize(&syn_item)) + Some(try!(ser::expand_derive_serialize(&syn_item))) } else { None }; let expanded_de = if de { - Some(de::expand_derive_deserialize(&syn_item)) + Some(try!(de::expand_derive_deserialize(&syn_item))) } else { None:: }; let syn_item = strip_serde_attrs(syn_item); - return quote!(#expanded_ser #expanded_de #syn_item).to_string(); + return Ok(quote!(#expanded_ser #expanded_de #syn_item).to_string()); fn strip_serde_derives(item: syn::MacroInput) -> (bool, bool, syn::MacroInput) { let mut ser = false; diff --git a/serde_codegen/src/ser.rs b/serde_codegen/src/ser.rs index de27e912..08aebb83 100644 --- a/serde_codegen/src/ser.rs +++ b/serde_codegen/src/ser.rs @@ -5,8 +5,10 @@ use bound; use internals::ast::{Body, Field, Item, Style, Variant}; use internals::{self, attr}; -pub fn expand_derive_serialize(item: &syn::MacroInput) -> Tokens { - let item = Item::from_ast(&internals::Ctxt::new(), item); +pub fn expand_derive_serialize(item: &syn::MacroInput) -> Result { + let ctxt = internals::Ctxt::new(); + let item = Item::from_ast(&ctxt, item); + try!(ctxt.check()); let impl_generics = build_impl_generics(&item); @@ -22,7 +24,7 @@ pub fn expand_derive_serialize(item: &syn::MacroInput) -> Tokens { let dummy_const = aster::id(format!("_IMPL_SERIALIZE_FOR_{}", item.ident)); - quote! { + Ok(quote! { #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] const #dummy_const: () = { extern crate serde as _serde; @@ -35,7 +37,7 @@ pub fn expand_derive_serialize(item: &syn::MacroInput) -> Tokens { } } }; - } + }) } // All the generics in the input, plus a bound `T: Serialize` for each generic diff --git a/serde_codegen_internals/src/ctxt.rs b/serde_codegen_internals/src/ctxt.rs index a516aec8..55ec5582 100644 --- a/serde_codegen_internals/src/ctxt.rs +++ b/serde_codegen_internals/src/ctxt.rs @@ -1,29 +1,43 @@ use std::fmt::Display; -use std::cell::Cell; +use std::cell::RefCell; #[derive(Default)] pub struct Ctxt { - err_count: Cell, + errors: RefCell>>, } impl Ctxt { pub fn new() -> Self { - Default::default() + Ctxt { + errors: RefCell::new(Some(Vec::new())), + } } pub fn error(&self, msg: T) { - println!("{}", msg); - self.err_count.set(self.err_count.get() + 1); + self.errors.borrow_mut().as_mut().unwrap().push(msg.to_string()); + } + + pub fn check(self) -> Result<(), String> { + let mut errors = self.errors.borrow_mut().take().unwrap(); + match errors.len() { + 0 => Ok(()), + 1 => Err(errors.pop().unwrap()), + n => { + let mut msg = format!("{} errors:", n); + for err in errors { + msg += "\n\t# "; + msg += &err; + } + Err(msg) + } + } } } impl Drop for Ctxt { fn drop(&mut self) { - let err_count = self.err_count.get(); - if err_count == 1 { - panic!("1 error"); - } else if err_count > 1 { - panic!("{} errors", err_count); + if self.errors.borrow().is_some() { + panic!("forgot to check for errors"); } } } diff --git a/serde_derive/src/lib.rs b/serde_derive/src/lib.rs index 5c80708a..0f435142 100644 --- a/serde_derive/src/lib.rs +++ b/serde_derive/src/lib.rs @@ -9,13 +9,17 @@ use rustc_macro::TokenStream; #[rustc_macro_derive(Serialize)] pub fn derive_serialize(input: TokenStream) -> TokenStream { let item = format!("#[derive(Serialize)]\n{}", input); - let expanded = serde_codegen::expand_single_item(&item); - expanded.parse().unwrap() + match serde_codegen::expand_single_item(&item) { + Ok(expanded) => expanded.parse().unwrap(), + Err(msg) => panic!(msg), + } } #[rustc_macro_derive(Deserialize)] pub fn derive_deserialize(input: TokenStream) -> TokenStream { let item = format!("#[derive(Deserialize)]\n{}", input); - let expanded = serde_codegen::expand_single_item(&item); - expanded.parse().unwrap() + match serde_codegen::expand_single_item(&item) { + Ok(expanded) => expanded.parse().unwrap(), + Err(msg) => panic!(msg), + } }