diff --git a/crates/oxc_macros/src/declare_oxc_secret.rs b/crates/oxc_macros/src/declare_oxc_secret.rs index ae9b7ff677524..c852adaec4444 100644 --- a/crates/oxc_macros/src/declare_oxc_secret.rs +++ b/crates/oxc_macros/src/declare_oxc_secret.rs @@ -1,11 +1,11 @@ use proc_macro::TokenStream; use quote::quote; -use syn::{ - parse::{Parse, ParseStream}, - Ident, LitFloat, LitInt, LitStr, Token, -}; +use syn::{parse::Parse, Ident, LitFloat, LitInt, LitStr, Token}; -use super::declare_oxc_lint::rule_name_converter; +use super::{ + declare_oxc_lint::rule_name_converter, + util::{eat_comma, parse_assert}, +}; pub struct SecretRuleMeta { struct_name: Ident, @@ -16,12 +16,12 @@ pub struct SecretRuleMeta { } impl Parse for SecretRuleMeta { - fn parse(mut input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream) -> syn::Result { let struct_name = input.parse()?; input.parse::()?; let description = input.parse()?; - eat_comma(&mut input)?; + eat_comma(&input)?; let mut rule = SecretRuleMeta { struct_name, @@ -33,46 +33,45 @@ impl Parse for SecretRuleMeta { while input.peek(Ident) { let ident = input.parse::()?; + #[allow(clippy::neg_cmp_op_on_partial_ord)] match ident.to_string().as_str() { "entropy" => { input.parse::()?; let entropy = input.parse::()?; - if entropy.base10_parse::()? < 0.0 { - return Err(syn::Error::new_spanned( - entropy, - "Entropy must be greater than or equal to 0.", - )); - } + parse_assert!( + entropy.base10_parse::()? >= 0.0, + entropy, + "Entropy must be greater than or equal to 0." + ); rule.entropy = Some(entropy); } "min_len" => { input.parse::()?; let min_len = input.parse::()?; - if min_len.base10_parse::()? < 1 { - return Err(syn::Error::new_spanned( - min_len, - "Minimum length cannot be zero.", - )); - } + parse_assert!( + min_len.base10_parse::()? > 0, + min_len, + "Minimum length must be greater than or equal to 1." + ); rule.min_len = Some(min_len); } "max_len" => { input.parse::()?; let max_len = input.parse::()?; - if max_len.base10_parse::()? < 1 { - return Err(syn::Error::new_spanned( - max_len, - "Maximum length cannot be zero.", - )); - } + parse_assert!( + max_len.base10_parse::()? > 0, + max_len, + "Maximum length cannot be zero." + ); rule.max_len = Some(max_len); } - _ => return Err(syn::Error::new_spanned( + _ => parse_assert!( + false, ident, - "Unexpected attribute. Only `entropy`, `min_len`, and `max_len` are allowed", - )), + "Unexpected attribute. Only `entropy`, `min_len`, and `max_len` are allowed." + ), } - eat_comma(&mut input)?; + eat_comma(&input)?; } // Ignore the rest @@ -81,12 +80,11 @@ impl Parse for SecretRuleMeta { if let (Some(min), Some(max)) = (rule.min_len.as_ref(), &rule.max_len.as_ref()) { let min = min.base10_parse::()?; let max = max.base10_parse::()?; - if min > max { - return Err(syn::Error::new_spanned( - max, - "Maximum length must be greater than or equal to minimum length.", - )); - } + parse_assert!( + min <= max, + max, + "Maximum length must be greater than or equal to minimum length." + ); } Ok(rule) @@ -155,10 +153,3 @@ pub fn declare_oxc_secret(meta: SecretRuleMeta) -> TokenStream { TokenStream::from(output) } - -fn eat_comma(input: &mut ParseStream) -> syn::Result<()> { - if input.peek(Token!(,)) { - input.parse::()?; - } - Ok(()) -} diff --git a/crates/oxc_macros/src/lib.rs b/crates/oxc_macros/src/lib.rs index f7ba6e5d9c32c..e69b1a5c462f1 100644 --- a/crates/oxc_macros/src/lib.rs +++ b/crates/oxc_macros/src/lib.rs @@ -1,6 +1,8 @@ use proc_macro::TokenStream; use syn::parse_macro_input; +#[macro_use] +mod util; mod declare_all_lint_rules; mod declare_oxc_lint; mod declare_oxc_secret; diff --git a/crates/oxc_macros/src/util.rs b/crates/oxc_macros/src/util.rs new file mode 100644 index 0000000000000..3b4f52c4a6adb --- /dev/null +++ b/crates/oxc_macros/src/util.rs @@ -0,0 +1,34 @@ +use syn::{parse::ParseStream, Result, Token}; + +/// Checks if `cond` is `true`, returning [`Err(syn::Error)`] with `msg` if it's not. +/// ## Example +/// ```ignore +/// use syn::{parse::Parse, LitStr}; +/// +/// struct Foo(LitStr); +/// +/// impl Parse for Foo { +/// fn parse(input: ParseStream) -> Result { +/// let s = input.parse::()?; +/// parse_assert!(s.value() == "foo", s, "Expected 'foo'"); +/// Ok(Foo(s)) +/// } +/// } +/// ``` +macro_rules! parse_assert { + ($cond:expr, $toks:expr, $msg:expr) => { + if !($cond) { + return Err(syn::Error::new_spanned($toks, $msg)); + } + }; +} +pub(crate) use parse_assert; + +/// Consume a comma token if it's present, noop otherwise +#[allow(clippy::trivially_copy_pass_by_ref)] +pub(crate) fn eat_comma(input: &ParseStream) -> Result<()> { + if input.peek(Token!(,)) { + input.parse::()?; + } + Ok(()) +}