From 178267c701c233542078c09fe6b19802f9642dbd Mon Sep 17 00:00:00 2001 From: HampusM Date: Mon, 30 Jan 2023 21:29:21 +0100 Subject: feat: improve macro error messages --- macros/src/injectable/macro_args.rs | 135 ++++++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 38 deletions(-) (limited to 'macros/src/injectable/macro_args.rs') diff --git a/macros/src/injectable/macro_args.rs b/macros/src/injectable/macro_args.rs index d730e0d..6582cc6 100644 --- a/macros/src/injectable/macro_args.rs +++ b/macros/src/injectable/macro_args.rs @@ -1,8 +1,10 @@ +use proc_macro2::Span; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; -use syn::{Token, TypePath}; +use syn::{Ident, Token, TypePath}; use crate::macro_flag::MacroFlag; +use crate::util::error::diagnostic_error_enum; use crate::util::iterator_ext::IteratorExt; pub const INJECTABLE_MACRO_FLAGS: &[&str] = @@ -14,9 +16,34 @@ pub struct InjectableMacroArgs pub flags: Punctuated, } +impl InjectableMacroArgs +{ + pub fn check_flags(&self) -> Result<(), InjectableMacroArgsError> + { + for flag in &self.flags { + if !INJECTABLE_MACRO_FLAGS.contains(&flag.flag.to_string().as_str()) { + return Err(InjectableMacroArgsError::UnknownFlag { + flag_ident: flag.flag.clone(), + }); + } + } + + if let Some((dupe_flag_first, dupe_flag_second)) = + self.flags.iter().find_duplicate() + { + return Err(InjectableMacroArgsError::DuplicateFlag { + first_flag_ident: dupe_flag_first.flag.clone(), + last_flag_span: dupe_flag_second.flag.span(), + }); + } + + Ok(()) + } +} + impl Parse for InjectableMacroArgs { - fn parse(input: ParseStream) -> syn::Result + fn parse(input: ParseStream) -> Result { let input_fork = input.fork(); @@ -50,31 +77,33 @@ impl Parse for InjectableMacroArgs let flags = Punctuated::::parse_terminated(input)?; - for flag in &flags { - let flag_str = flag.flag.to_string(); - - if !INJECTABLE_MACRO_FLAGS.contains(&flag_str.as_str()) { - return Err(input.error(format!( - "Unknown flag '{}'. Expected one of [ {} ]", - flag_str, - INJECTABLE_MACRO_FLAGS.join(",") - ))); - } - } - - let flag_names = flags - .iter() - .map(|flag| flag.flag.to_string()) - .collect::>(); - - if let Some(dupe_flag_name) = flag_names.iter().find_duplicate() { - return Err(input.error(format!("Duplicate flag '{dupe_flag_name}'"))); - } - Ok(Self { interface, flags }) } } +diagnostic_error_enum! { +pub enum InjectableMacroArgsError +{ + #[error("Unknown flag '{flag_ident}'"), span = flag_ident.span()] + #[ + help("Expected one of: {}", INJECTABLE_MACRO_FLAGS.join(", ")), + span = flag_ident.span() + ] + UnknownFlag + { + flag_ident: Ident + }, + + #[error("Duplicate flag '{first_flag_ident}'"), span = first_flag_ident.span()] + #[note("Previously mentioned here"), span = last_flag_span] + DuplicateFlag + { + first_flag_ident: Ident, + last_flag_span: Span + }, +} +} + #[cfg(test)] mod tests { @@ -188,30 +217,60 @@ mod tests } #[test] - fn cannot_parse_with_invalid_flag() + fn can_parse_with_unknown_flag() -> Result<(), Box> { let input_args = quote! { IFoo, haha = true, async = false }; - assert!(matches!(parse2::(input_args), Err(_))); + assert!(parse2::(input_args).is_ok()); + + Ok(()) } #[test] - fn cannot_parse_with_duplicate_flag() + fn can_parse_with_duplicate_flag() { - assert!(matches!( - parse2::(quote! { - IFoo, async = false, no_doc_hidden = true, async = false - }), - Err(_) - )); + assert!(parse2::(quote! { + IFoo, async = false, no_doc_hidden = true, async = false + }) + .is_ok()); + + assert!(parse2::(quote! { + IFoo, async = true , no_doc_hidden = true, async = false + }) + .is_ok()); + } - assert!(matches!( - parse2::(quote! { - IFoo, async = true , no_doc_hidden = true, async = false - }), - Err(_) - )); + #[test] + fn check_flags_fail_with_unknown_flag() -> Result<(), Box> + { + let input_args = quote! { + IFoo, haha = true, async = false + }; + + let injectable_macro_args = parse2::(input_args)?; + + assert!(injectable_macro_args.check_flags().is_err()); + + Ok(()) + } + + #[test] + fn check_flags_fail_with_duplicate_flag() -> Result<(), Box> + { + let macro_args = parse2::(quote! { + IFoo, async = false, no_doc_hidden = true, async = false + })?; + + assert!(macro_args.check_flags().is_err()); + + let macro_args_two = parse2::(quote! { + IFoo, async = true , no_doc_hidden = true, async = false + })?; + + assert!(macro_args_two.check_flags().is_err()); + + Ok(()) } } -- cgit v1.2.3-18-g5258