diff --git a/Cargo.toml b/Cargo.toml index 3da36c5..20a1c5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,4 @@ proc-macro = true quote = "1" syn = "1" proc-macro2 = { version = "1", default-features = false } +proc-macro-error = "0.4" diff --git a/src/generate.rs b/src/generate.rs index e5da6da..4e73bf2 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -1,6 +1,7 @@ use proc_macro2::TokenStream as TokenStream2; use proc_macro2::{Ident, Span}; -use syn::{self, Field, Lit, Meta, MetaNameValue, Visibility}; +use proc_macro_error::abort; +use syn::{self, spanned::Spanned, Field, Lit, Meta, MetaNameValue, Visibility}; pub struct GenParams { pub attribute_name: &'static str, @@ -35,10 +36,10 @@ pub fn parse_visibility(attr: Option<&Meta>, meta_name: &str) -> Option { if path.is_ident(meta_name) { - s.value() - .split(' ') - .find(|v| *v != "with_prefix") - .map(|v| syn::parse(v.parse().unwrap()).expect("invalid visibility found")) + s.value().split(' ').find(|v| *v != "with_prefix").map(|v| { + syn::parse_str(v) + .unwrap_or_else(|_| abort!(s.span(), "invalid visibility found")) + }) } else { None } @@ -53,16 +54,11 @@ fn has_prefix_attr(f: &Field) -> bool { let inner = f .attrs .iter() - .filter_map(|v| { - let meta = v.parse_meta().expect("Could not get attribute"); - if ["get", "get_copy"] + .filter_map(|v| v.parse_meta().ok()) + .filter(|meta| { + ["get", "get_copy"] .iter() .any(|ident| meta.path().is_ident(ident)) - { - Some(meta) - } else { - None - } }) .last(); match inner { @@ -82,7 +78,7 @@ pub fn implement(field: &Field, mode: &GenMode, params: &GenParams) -> TokenStre let field_name = field .clone() .ident - .expect("Expected the field to have a name"); + .unwrap_or_else(|| abort!(field.span(), "Expected the field to have a name")); let fn_name = Ident::new( &format!( @@ -104,8 +100,8 @@ pub fn implement(field: &Field, mode: &GenMode, params: &GenParams) -> TokenStre let attr = field .attrs .iter() - .filter_map(|v| { - let meta = v.parse_meta().expect("attribute"); + .filter_map(|v| v.parse_meta().ok().map(|meta| (v, meta))) + .filter_map(|(v, meta)| { if meta.path().is_ident("doc") { doc.push(v); None diff --git a/src/lib.rs b/src/lib.rs index eca7881..cfba310 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -148,15 +148,17 @@ extern crate proc_macro2; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; +use proc_macro_error::{abort_call_site, proc_macro_error, ResultExt}; use syn::{DataStruct, DeriveInput, Meta}; mod generate; use crate::generate::{GenMode, GenParams}; #[proc_macro_derive(Getters, attributes(get, with_prefix))] +#[proc_macro_error] pub fn getters(input: TokenStream) -> TokenStream { // Parse the string representation - let ast: DeriveInput = syn::parse(input).expect("Couldn't parse for getters"); + let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for getters"); let params = GenParams { attribute_name: "get", fn_name_prefix: "", @@ -172,9 +174,10 @@ pub fn getters(input: TokenStream) -> TokenStream { } #[proc_macro_derive(CopyGetters, attributes(get_copy, with_prefix))] +#[proc_macro_error] pub fn copy_getters(input: TokenStream) -> TokenStream { // Parse the string representation - let ast: DeriveInput = syn::parse(input).expect("Couldn't parse for getters"); + let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for getters"); let params = GenParams { attribute_name: "get_copy", fn_name_prefix: "", @@ -190,9 +193,10 @@ pub fn copy_getters(input: TokenStream) -> TokenStream { } #[proc_macro_derive(MutGetters, attributes(get_mut))] +#[proc_macro_error] pub fn mut_getters(input: TokenStream) -> TokenStream { // Parse the string representation - let ast: DeriveInput = syn::parse(input).expect("Couldn't parse for getters"); + let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for getters"); let params = GenParams { attribute_name: "get_mut", fn_name_prefix: "", @@ -207,9 +211,10 @@ pub fn mut_getters(input: TokenStream) -> TokenStream { } #[proc_macro_derive(Setters, attributes(set))] +#[proc_macro_error] pub fn setters(input: TokenStream) -> TokenStream { // Parse the string representation - let ast: DeriveInput = syn::parse(input).expect("Couldn't parse for setters"); + let ast: DeriveInput = syn::parse(input).expect_or_abort("Couldn't parse for setters"); let params = GenParams { attribute_name: "set", fn_name_prefix: "set_", @@ -227,14 +232,8 @@ pub fn setters(input: TokenStream) -> TokenStream { fn parse_global_attr(attrs: &[syn::Attribute], attribute_name: &str) -> Option { attrs .iter() - .filter_map(|v| { - let meta = v.parse_meta().expect("attribute"); - if meta.path().is_ident(attribute_name) { - Some(meta) - } else { - None - } - }) + .filter_map(|v| v.parse_meta().ok()) // non "meta" attributes are not our concern + .filter(|meta| meta.path().is_ident(attribute_name)) .last() } @@ -257,6 +256,6 @@ fn produce(ast: &DeriveInput, mode: &GenMode, params: &GenParams) -> TokenStream } } else { // Nope. This is an Enum. We cannot handle these! - panic!("#[derive(Getters)] is only defined for structs, not for enums!"); + abort_call_site!("#[derive(Getters)] is only defined for structs, not for enums!"); } }