| 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), |
| + )) |
| } |
| } |
| } |