diff --git a/Cargo.lock b/Cargo.lock index 2e25d73d2..289d88dc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -285,9 +285,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if", "crossbeam-utils", @@ -295,9 +295,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -306,9 +306,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg", "cfg-if", @@ -319,9 +319,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", ] @@ -612,9 +612,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jemalloc-sys" @@ -696,9 +696,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.8.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] @@ -1145,9 +1145,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.36.9" +version = "0.36.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", "errno", @@ -1177,9 +1177,9 @@ checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "ryu-js" @@ -1251,9 +1251,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.18" +version = "0.9.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05864bf272adeeef0f3e0ff93408e53fc94131201caab11dfd6045e2e8570d1b" +checksum = "f82e6c8c047aa50a7328632d067bcae6ef38772a79e28daf32f735e0e4f3dd10" dependencies = [ "indexmap", "itoa", @@ -1459,9 +1459,9 @@ checksum = "238a3d5702128479aa8f25de86d12dde3ef71859109b6c1be6ce62dd4e76b160" [[package]] name = "unicode-ident" -version = "1.0.7" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-linebreak" diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 64340c76e..f6b2c3f14 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -1,104 +1,10 @@ -mod constructor_super; -mod eq_eq_eq; -mod for_direction; -mod no_array_constructor; -mod no_debugger; -mod no_empty; -mod no_empty_pattern; -mod deepscan { - pub mod uninvoked_array_callback; -} - -pub use constructor_super::ConstructorSuper; -pub use deepscan::uninvoked_array_callback::UninvokedArrayCallback; -pub use eq_eq_eq::EqEqEq; -pub use for_direction::ForDirection; -pub use no_array_constructor::NoArrayConstructor; -pub use no_debugger::NoDebugger; -pub use no_empty::NoEmpty; -pub use no_empty_pattern::NoEmptyPattern; - -use crate::{context::LintContext, rule::Rule, rule::RuleMeta, AstNode}; - -lazy_static::lazy_static! { - pub static ref RULES: Vec = vec![ - RuleEnum::EqEqEq(EqEqEq::default()), - RuleEnum::ConstructorSuper(ConstructorSuper::default()), - RuleEnum::NoDebugger(NoDebugger::default()), - RuleEnum::NoEmpty(NoEmpty::default()), - RuleEnum::NoArrayConstructor(NoArrayConstructor::default()), - RuleEnum::NoEmptyPattern(NoEmptyPattern::default()), - RuleEnum::UninvokedArrayCallback(UninvokedArrayCallback::default()), - RuleEnum::ForDirection(ForDirection::default()), - ]; -} - -#[derive(Debug, Clone)] -#[allow(clippy::enum_variant_names)] -pub enum RuleEnum { - EqEqEq(EqEqEq), - ConstructorSuper(ConstructorSuper), - NoDebugger(NoDebugger), - NoEmpty(NoEmpty), - NoArrayConstructor(NoArrayConstructor), - NoEmptyPattern(NoEmptyPattern), - UninvokedArrayCallback(UninvokedArrayCallback), - ForDirection(ForDirection), -} - -impl RuleEnum { - pub fn name(&self) -> &'static str { - match self { - Self::EqEqEq(_) => EqEqEq::NAME, - Self::ConstructorSuper(_) => ConstructorSuper::NAME, - Self::NoDebugger(_) => NoDebugger::NAME, - Self::NoEmpty(_) => NoEmpty::NAME, - Self::NoArrayConstructor(_) => NoArrayConstructor::NAME, - Self::NoEmptyPattern(_) => NoEmptyPattern::NAME, - Self::UninvokedArrayCallback(_) => UninvokedArrayCallback::NAME, - Self::ForDirection(_) => ForDirection::NAME, - } - } - - pub fn read_json(&self, maybe_value: Option) -> Self { - match self { - Self::EqEqEq(_) => { - Self::EqEqEq(maybe_value.map(EqEqEq::from_configuration).unwrap_or_default()) - } - Self::ConstructorSuper(_) => Self::ConstructorSuper( - maybe_value.map(ConstructorSuper::from_configuration).unwrap_or_default(), - ), - Self::NoDebugger(_) => Self::NoDebugger( - maybe_value.map(NoDebugger::from_configuration).unwrap_or_default(), - ), - Self::NoEmpty(_) => { - Self::NoEmpty(maybe_value.map(NoEmpty::from_configuration).unwrap_or_default()) - } - Self::NoArrayConstructor(_) => Self::NoArrayConstructor( - maybe_value.map(NoArrayConstructor::from_configuration).unwrap_or_default(), - ), - Self::NoEmptyPattern(_) => Self::NoEmptyPattern( - maybe_value.map(NoEmptyPattern::from_configuration).unwrap_or_default(), - ), - Self::UninvokedArrayCallback(_) => Self::UninvokedArrayCallback( - maybe_value.map(UninvokedArrayCallback::from_configuration).unwrap_or_default(), - ), - Self::ForDirection(_) => Self::ForDirection( - maybe_value.map(ForDirection::from_configuration).unwrap_or_default(), - ), - } - } - - pub fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - match self { - Self::EqEqEq(rule) => rule.run(node, ctx), - Self::ConstructorSuper(rule) => rule.run(node, ctx), - Self::NoDebugger(rule) => rule.run(node, ctx), - Self::NoEmpty(rule) => rule.run(node, ctx), - Self::NoArrayConstructor(rule) => rule.run(node, ctx), - Self::NoEmptyPattern(rule) => rule.run(node, ctx), - Self::UninvokedArrayCallback(rule) => rule.run(node, ctx), - Self::ForDirection(rule) => rule.run(node, ctx), - } - } +oxc_macros::declare_all_lint_rules! { + constructor_super, + eq_eq_eq, + for_direction, + no_debugger, + no_array_constructor, + no_empty, + no_empty_pattern, + deepscan::uninvoked_array_callback, } diff --git a/crates/oxc_macros/src/declare_all_lint_rules.rs b/crates/oxc_macros/src/declare_all_lint_rules.rs new file mode 100644 index 000000000..1fa71298a --- /dev/null +++ b/crates/oxc_macros/src/declare_all_lint_rules.rs @@ -0,0 +1,121 @@ +use convert_case::{Case, Casing}; +use proc_macro2::TokenStream; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::Result; + +pub struct LintRuleMeta { + name: syn::Ident, + path: syn::Path, +} + +impl LintRuleMeta { + pub fn mod_stmt(&self) -> TokenStream { + let mut segments = self.path.segments.iter().rev().peekable(); + let first = &segments.next().unwrap().ident; + let mut stmts = quote! {mod #first;}; + if segments.peek().is_some() { + stmts = quote! {pub #stmts}; + } + + while let Some(segment) = segments.next() { + let ident = &segment.ident; + + stmts = quote! { + mod #ident { #stmts } + }; + + if segments.peek().is_some() { + stmts = quote! { + pub #stmts + }; + } + } + + stmts + } + + pub fn use_stmt(&self) -> TokenStream { + let mut path = self.path.clone(); + path.segments.push(self.name.clone().into()); + + quote! { + pub use #path; + } + } +} + +impl Parse for LintRuleMeta { + fn parse(input: ParseStream<'_>) -> Result { + let path = input.parse::()?; + let name = syn::parse_str( + &path.segments.iter().last().unwrap().ident.to_string().to_case(Case::Pascal), + ) + .unwrap(); + Ok(Self { path, name }) + } +} + +pub struct AllLintRulesMeta { + rules: Vec, +} + +impl Parse for AllLintRulesMeta { + fn parse(input: ParseStream<'_>) -> Result { + let rules = input + .parse_terminated::(LintRuleMeta::parse)? + .into_iter() + .collect(); + + Ok(Self { rules }) + } +} + +pub fn declare_all_lint_rules(metadata: AllLintRulesMeta) -> TokenStream { + let AllLintRulesMeta { rules } = metadata; + + let mod_stmts = rules.iter().map(|rule| rule.mod_stmt()); + let use_stmts = rules.iter().map(|rule| rule.use_stmt()); + let struct_names = rules.iter().map(|rule| &rule.name).collect::>(); + + quote! { + #(#mod_stmts)* + #(#use_stmts)* + + use crate::{context::LintContext, rule::Rule, rule::RuleMeta, AstNode}; + + #[derive(Debug, Clone)] + #[allow(clippy::enum_variant_names)] + pub enum RuleEnum { + #(#struct_names(#struct_names)),* + } + + impl RuleEnum { + pub const fn name(&self) -> &'static str { + match self { + #(Self::#struct_names(_) => #struct_names::NAME),* + } + } + + pub fn read_json(&self, maybe_value: Option) -> Self { + match self { + #(Self::#struct_names(_) => Self::#struct_names( + maybe_value.map(#struct_names::from_configuration).unwrap_or_default(), + )),* + } + } + + pub fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + match self { + #(Self::#struct_names(rule) => rule.run(node, ctx)),* + } + } + } + + lazy_static::lazy_static! { + pub static ref RULES: Vec = vec![ + #(RuleEnum::#struct_names(#struct_names::default())),* + ]; + } + } +} diff --git a/crates/oxc_macros/src/lib.rs b/crates/oxc_macros/src/lib.rs index c4af47788..1b1cf024d 100644 --- a/crates/oxc_macros/src/lib.rs +++ b/crates/oxc_macros/src/lib.rs @@ -1,5 +1,6 @@ use syn::parse_macro_input; +mod declare_all_lint_rules; mod declare_oxc_lint; /// Macro used to declare an oxc lint rule @@ -50,3 +51,10 @@ pub fn declare_oxc_lint_test(input: proc_macro::TokenStream) -> proc_macro::Toke declare_oxc_lint::declare_oxc_lint(metadata).into() } + +#[proc_macro] +pub fn declare_all_lint_rules(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let metadata = parse_macro_input!(input as declare_all_lint_rules::AllLintRulesMeta); + + declare_all_lint_rules::declare_all_lint_rules(metadata).into() +}