|  | diff --git a/Cargo.toml b/Cargo.toml | 
|  | index 2e33871..388f042 100644 | 
|  | --- a/Cargo.toml | 
|  | +++ b/Cargo.toml | 
|  | @@ -24,8 +24,6 @@ repository = "https://git.sr.ht/~ireas/merge-rs/tree/master/merge_derive" | 
|  |  | 
|  | [lib] | 
|  | proc-macro = true | 
|  | -[dependencies.proc-macro-error] | 
|  | -version = "1.0" | 
|  |  | 
|  | [dependencies.proc-macro2] | 
|  | version = "1.0" | 
|  | @@ -34,4 +32,4 @@ version = "1.0" | 
|  | version = "1.0" | 
|  |  | 
|  | [dependencies.syn] | 
|  | -version = "1.0" | 
|  | +version = "2.0" | 
|  | diff --git a/src/lib.rs b/src/lib.rs | 
|  | index 75732f9..11f5b49 100644 | 
|  | --- a/src/lib.rs | 
|  | +++ b/src/lib.rs | 
|  | @@ -11,9 +11,9 @@ | 
|  | extern crate proc_macro; | 
|  |  | 
|  | use proc_macro2::TokenStream; | 
|  | -use proc_macro_error::{abort, abort_call_site, dummy::set_dummy, proc_macro_error, ResultExt}; | 
|  | use quote::{quote, quote_spanned}; | 
|  | -use syn::Token; | 
|  | +use std::convert::TryFrom; | 
|  | +use syn::{Error, Result, Token}; | 
|  |  | 
|  | struct Field { | 
|  | name: syn::Member, | 
|  | @@ -33,48 +33,51 @@ enum FieldAttr { | 
|  | } | 
|  |  | 
|  | #[proc_macro_derive(Merge, attributes(merge))] | 
|  | -#[proc_macro_error] | 
|  | pub fn merge_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { | 
|  | let ast = syn::parse(input).unwrap(); | 
|  | -    impl_merge(&ast).into() | 
|  | +    impl_merge(&ast) | 
|  | +        .unwrap_or_else(Error::into_compile_error) | 
|  | +        .into() | 
|  | } | 
|  |  | 
|  | -fn impl_merge(ast: &syn::DeriveInput) -> TokenStream { | 
|  | +fn impl_merge(ast: &syn::DeriveInput) -> Result<TokenStream> { | 
|  | let name = &ast.ident; | 
|  |  | 
|  | -    set_dummy(quote! { | 
|  | -        impl ::merge::Merge for #name { | 
|  | -            fn merge(&mut self, other: Self) { | 
|  | -                unimplemented!() | 
|  | -            } | 
|  | -        } | 
|  | -    }); | 
|  | - | 
|  | if let syn::Data::Struct(syn::DataStruct { ref fields, .. }) = ast.data { | 
|  | impl_merge_for_struct(name, fields) | 
|  | } else { | 
|  | -        abort_call_site!("merge::Merge can only be derived for structs") | 
|  | +        Err(Error::new_spanned( | 
|  | +            ast, | 
|  | +            "merge::Merge can only be derived for structs", | 
|  | +        )) | 
|  | } | 
|  | } | 
|  |  | 
|  | -fn impl_merge_for_struct(name: &syn::Ident, fields: &syn::Fields) -> TokenStream { | 
|  | -    let assignments = gen_assignments(fields); | 
|  | +fn impl_merge_for_struct(name: &syn::Ident, fields: &syn::Fields) -> Result<TokenStream> { | 
|  | +    let assignments = gen_assignments(fields)?; | 
|  |  | 
|  | -    quote! { | 
|  | +    Ok(quote! { | 
|  | impl ::merge::Merge for #name { | 
|  | fn merge(&mut self, other: Self) { | 
|  | #assignments | 
|  | } | 
|  | } | 
|  | -    } | 
|  | +    }) | 
|  | } | 
|  |  | 
|  | -fn gen_assignments(fields: &syn::Fields) -> TokenStream { | 
|  | -    let fields = fields.iter().enumerate().map(Field::from); | 
|  | -    let assignments = fields.filter(|f| !f.attrs.skip).map(|f| gen_assignment(&f)); | 
|  | -    quote! { | 
|  | +fn gen_assignments(fields: &syn::Fields) -> Result<TokenStream> { | 
|  | +    let fields = fields | 
|  | +        .iter() | 
|  | +        .enumerate() | 
|  | +        .map(Field::try_from) | 
|  | +        .collect::<Result<Vec<_>>>()?; | 
|  | +    let assignments = fields | 
|  | +        .iter() | 
|  | +        .filter(|f| !f.attrs.skip) | 
|  | +        .map(|f| gen_assignment(&f)); | 
|  | +    Ok(quote! { | 
|  | #( #assignments )* | 
|  | -    } | 
|  | +    }) | 
|  | } | 
|  |  | 
|  | fn gen_assignment(field: &Field) -> TokenStream { | 
|  | @@ -88,48 +91,48 @@ fn gen_assignment(field: &Field) -> TokenStream { | 
|  | } | 
|  | } | 
|  |  | 
|  | -impl From<(usize, &syn::Field)> for Field { | 
|  | -    fn from(data: (usize, &syn::Field)) -> Self { | 
|  | +impl TryFrom<(usize, &syn::Field)> for Field { | 
|  | +    type Error = syn::Error; | 
|  | + | 
|  | +    fn try_from(data: (usize, &syn::Field)) -> std::result::Result<Self, Self::Error> { | 
|  | use syn::spanned::Spanned; | 
|  |  | 
|  | let (index, field) = data; | 
|  | -        Field { | 
|  | +        Ok(Field { | 
|  | name: if let Some(ident) = &field.ident { | 
|  | syn::Member::Named(ident.clone()) | 
|  | } else { | 
|  | syn::Member::Unnamed(index.into()) | 
|  | }, | 
|  | span: field.span(), | 
|  | -            attrs: field.attrs.iter().into(), | 
|  | -        } | 
|  | +            attrs: FieldAttrs::new(field.attrs.iter())?, | 
|  | +        }) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl FieldAttrs { | 
|  | -    fn apply(&mut self, attr: FieldAttr) { | 
|  | -        match attr { | 
|  | -            FieldAttr::Skip => self.skip = true, | 
|  | -            FieldAttr::Strategy(path) => self.strategy = Some(path), | 
|  | -        } | 
|  | -    } | 
|  | -} | 
|  | - | 
|  | -impl<'a, I: Iterator<Item = &'a syn::Attribute>> From<I> for FieldAttrs { | 
|  | -    fn from(iter: I) -> Self { | 
|  | +    fn new<'a, I: Iterator<Item = &'a syn::Attribute>>(iter: I) -> Result<Self> { | 
|  | let mut field_attrs = Self::default(); | 
|  |  | 
|  | for attr in iter { | 
|  | -            if !attr.path.is_ident("merge") { | 
|  | +            if !attr.path().is_ident("merge") { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | let parser = syn::punctuated::Punctuated::<FieldAttr, Token![,]>::parse_terminated; | 
|  | -            for attr in attr.parse_args_with(parser).unwrap_or_abort() { | 
|  | +            for attr in attr.parse_args_with(parser)? { | 
|  | field_attrs.apply(attr); | 
|  | } | 
|  | } | 
|  |  | 
|  | -        field_attrs | 
|  | +        Ok(field_attrs) | 
|  | +    } | 
|  | + | 
|  | +    fn apply(&mut self, attr: FieldAttr) { | 
|  | +        match attr { | 
|  | +            FieldAttr::Skip => self.skip = true, | 
|  | +            FieldAttr::Strategy(path) => self.strategy = Some(path), | 
|  | +        } | 
|  | } | 
|  | } | 
|  |  | 
|  | @@ -144,7 +147,10 @@ impl syn::parse::Parse for FieldAttr { | 
|  | let path: syn::Path = input.parse()?; | 
|  | Ok(FieldAttr::Strategy(path)) | 
|  | } else { | 
|  | -            abort!(name, "Unexpected attribute: {}", name) | 
|  | +            Err(Error::new_spanned( | 
|  | +                &name, | 
|  | +                format!("Unexpected attribute: {}", name), | 
|  | +            )) | 
|  | } | 
|  | } | 
|  | } |