From ebed2b0182221ccf0b78f8f83cca6fbcc282e34d Mon Sep 17 00:00:00 2001 From: Cam McHenry Date: Mon, 25 Aug 2025 21:34:41 -0400 Subject: [PATCH 1/2] perf(linter): extract node types from more rules --- crates/oxc_linter/src/rule.rs | 47 +++- crates/oxc_linter/src/rule_runner_impls.rs | 283 ++++++++++++++------- tasks/linter_codegen/src/main.rs | 167 +++++++++--- 3 files changed, 366 insertions(+), 131 deletions(-) diff --git a/crates/oxc_linter/src/rule.rs b/crates/oxc_linter/src/rule.rs index 554c50f9c48eb..ceeda8972c8f8 100644 --- a/crates/oxc_linter/src/rule.rs +++ b/crates/oxc_linter/src/rule.rs @@ -369,8 +369,39 @@ mod test { &[MethodDefinition], ); assert_rule_runs_on_node_types( - &import::no_mutable_exports::NoMutableExports, - &[ExportNamedDeclaration, ExportDefaultDeclaration], + &eslint::no_array_constructor::NoArrayConstructor, + &[CallExpression, NewExpression], + ); + assert_rule_runs_on_node_types( + &eslint::no_console::NoConsole::default(), + &[StaticMemberExpression, ComputedMemberExpression], + ); + assert_rule_runs_on_node_types( + &eslint::no_extra_label::NoExtraLabel, + &[BreakStatement, ContinueStatement], + ); + assert_rule_runs_on_node_types( + &eslint::getter_return::GetterReturn::default(), + &[Function, ArrowFunctionExpression], + ); + assert_rule_runs_on_node_types( + &eslint::new_cap::NewCap::default(), + &[NewExpression, CallExpression], + ); + assert_rule_runs_on_node_types( + &eslint::no_cond_assign::NoCondAssign::default(), + &[ + IfStatement, + WhileStatement, + DoWhileStatement, + ForStatement, + ConditionalExpression, + AssignmentExpression, + ], + ); + assert_rule_runs_on_node_types( + &import::no_webpack_loader_syntax::NoWebpackLoaderSyntax, + &[CallExpression, ImportDeclaration], ); assert_rule_runs_on_node_types( &jest::prefer_jest_mocked::PreferJestMocked, @@ -393,6 +424,10 @@ mod test { &oxc::bad_bitwise_operator::BadBitwiseOperator, &[BinaryExpression, AssignmentExpression], ); + assert_rule_runs_on_node_types( + &oxc::only_used_in_recursion::OnlyUsedInRecursion, + &[Function, ArrowFunctionExpression], + ); assert_rule_runs_on_node_types( &promise::prefer_await_to_callbacks::PreferAwaitToCallbacks, &[CallExpression, Function, ArrowFunctionExpression], @@ -405,6 +440,10 @@ mod test { &typescript::ban_types::BanTypes, &[TSTypeReference, TSTypeLiteral], ); + assert_rule_runs_on_node_types( + &typescript::no_wrapper_object_types::NoWrapperObjectTypes, + &[TSTypeReference, TSClassImplements, TSInterfaceHeritage], + ); assert_rule_runs_on_node_types( &unicorn::explicit_length_check::ExplicitLengthCheck::default(), &[StaticMemberExpression], @@ -413,6 +452,10 @@ mod test { &unicorn::no_zero_fractions::NoZeroFractions, &[NumericLiteral], ); + assert_rule_runs_on_node_types( + &unicorn::prefer_array_find::PreferArrayFind, + &[AssignmentExpression, CallExpression, ComputedMemberExpression, VariableDeclarator], + ); assert!(!&jest::max_expects::MaxExpects::ONLY_RUNS_ON_NODES); } diff --git a/crates/oxc_linter/src/rule_runner_impls.rs b/crates/oxc_linter/src/rule_runner_impls.rs index 2b162ff46b618..58326287998be 100644 --- a/crates/oxc_linter/src/rule_runner_impls.rs +++ b/crates/oxc_linter/src/rule_runner_impls.rs @@ -9,8 +9,9 @@ use oxc_semantic::AstTypesBitset; use oxc_ast::AstType; impl RuleRunner for crate::rules::eslint::array_callback_return::ArrayCallbackReturn { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::ArrowFunctionExpression, AstType::Function]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -118,8 +119,9 @@ impl RuleRunner for crate::rules::eslint::func_style::FuncStyle { } } impl RuleRunner for crate::rules::eslint::getter_return::GetterReturn { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::ArrowFunctionExpression, AstType::Function]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -224,8 +226,9 @@ impl RuleRunner for crate::rules::eslint::max_params::MaxParams { } } impl RuleRunner for crate::rules::eslint::new_cap::NewCap { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::NewExpression]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -242,8 +245,9 @@ impl RuleRunner for crate::rules::eslint::no_alert::NoAlert { } } impl RuleRunner for crate::rules::eslint::no_array_constructor::NoArrayConstructor { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::NewExpression]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -260,8 +264,9 @@ impl RuleRunner for crate::rules::eslint::no_async_promise_executor::NoAsyncProm } } impl RuleRunner for crate::rules::eslint::no_await_in_loop::NoAwaitInLoop { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::AwaitExpression, AstType::ForOfStatement]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -319,8 +324,15 @@ impl RuleRunner for crate::rules::eslint::no_compare_neg_zero::NoCompareNegZero } } impl RuleRunner for crate::rules::eslint::no_cond_assign::NoCondAssign { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::AssignmentExpression, + AstType::ConditionalExpression, + AstType::DoWhileStatement, + AstType::ForStatement, + AstType::IfStatement, + AstType::WhileStatement, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -328,8 +340,11 @@ impl RuleRunner for crate::rules::eslint::no_cond_assign::NoCondAssign { } } impl RuleRunner for crate::rules::eslint::no_console::NoConsole { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::ComputedMemberExpression, + AstType::StaticMemberExpression, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -481,8 +496,9 @@ impl RuleRunner for crate::rules::eslint::no_else_return::NoElseReturn { } } impl RuleRunner for crate::rules::eslint::no_empty::NoEmpty { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::BlockStatement, AstType::SwitchStatement]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -535,8 +551,12 @@ impl RuleRunner for crate::rules::eslint::no_eq_null::NoEqNull { } } impl RuleRunner for crate::rules::eslint::no_eval::NoEval { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::CallExpression, + AstType::Program, + AstType::ThisExpression, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -571,8 +591,9 @@ impl RuleRunner for crate::rules::eslint::no_extra_bind::NoExtraBind { } } impl RuleRunner for crate::rules::eslint::no_extra_boolean_cast::NoExtraBooleanCast { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::UnaryExpression]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -580,8 +601,9 @@ impl RuleRunner for crate::rules::eslint::no_extra_boolean_cast::NoExtraBooleanC } } impl RuleRunner for crate::rules::eslint::no_extra_label::NoExtraLabel { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::BreakStatement, AstType::ContinueStatement]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -625,8 +647,9 @@ impl RuleRunner for crate::rules::eslint::no_import_assign::NoImportAssign { } } impl RuleRunner for crate::rules::eslint::no_inner_declarations::NoInnerDeclarations { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::Function, AstType::VariableDeclaration]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -670,8 +693,12 @@ impl RuleRunner for crate::rules::eslint::no_label_var::NoLabelVar { } } impl RuleRunner for crate::rules::eslint::no_labels::NoLabels { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::BreakStatement, + AstType::ContinueStatement, + AstType::LabeledStatement, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -697,8 +724,8 @@ impl RuleRunner for crate::rules::eslint::no_lonely_if::NoLonelyIf { } } impl RuleRunner for crate::rules::eslint::no_loss_of_precision::NoLossOfPrecision { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[AstType::NumericLiteral]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -733,8 +760,9 @@ impl RuleRunner for crate::rules::eslint::no_multi_str::NoMultiStr { } } impl RuleRunner for crate::rules::eslint::no_negated_condition::NoNegatedCondition { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::ConditionalExpression, AstType::IfStatement]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -797,8 +825,9 @@ impl RuleRunner for crate::rules::eslint::no_nonoctal_decimal_escape::NoNonoctal } } impl RuleRunner for crate::rules::eslint::no_obj_calls::NoObjCalls { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::NewExpression]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -806,8 +835,9 @@ impl RuleRunner for crate::rules::eslint::no_obj_calls::NoObjCalls { } } impl RuleRunner for crate::rules::eslint::no_object_constructor::NoObjectConstructor { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::NewExpression]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -851,8 +881,12 @@ impl RuleRunner for crate::rules::eslint::no_redeclare::NoRedeclare { } } impl RuleRunner for crate::rules::eslint::no_regex_spaces::NoRegexSpaces { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::CallExpression, + AstType::NewExpression, + AstType::RegExpLiteral, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -890,8 +924,9 @@ impl RuleRunner for crate::rules::eslint::no_return_assign::NoReturnAssign { } } impl RuleRunner for crate::rules::eslint::no_script_url::NoScriptUrl { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::StringLiteral, AstType::TemplateLiteral]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -1061,8 +1096,26 @@ impl RuleRunner for crate::rules::eslint::no_unsafe_negation::NoUnsafeNegation { } } impl RuleRunner for crate::rules::eslint::no_unsafe_optional_chaining::NoUnsafeOptionalChaining { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::Argument, + AstType::ArrayExpression, + AstType::AssignmentExpression, + AstType::AssignmentPattern, + AstType::AssignmentTargetWithDefault, + AstType::BinaryExpression, + AstType::CallExpression, + AstType::Class, + AstType::ComputedMemberExpression, + AstType::ForOfStatement, + AstType::NewExpression, + AstType::PrivateFieldExpression, + AstType::StaticMemberExpression, + AstType::TaggedTemplateExpression, + AstType::UnaryExpression, + AstType::VariableDeclarator, + AstType::WithStatement, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -1154,8 +1207,12 @@ impl RuleRunner for crate::rules::eslint::no_useless_constructor::NoUselessConst } } impl RuleRunner for crate::rules::eslint::no_useless_escape::NoUselessEscape { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::RegExpLiteral, + AstType::StringLiteral, + AstType::TemplateLiteral, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -1215,8 +1272,9 @@ impl RuleRunner for crate::rules::eslint::operator_assignment::OperatorAssignmen } } impl RuleRunner for crate::rules::eslint::prefer_destructuring::PreferDestructuring { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::AssignmentExpression, AstType::VariableDeclarator]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -1373,8 +1431,13 @@ impl RuleRunner for crate::rules::eslint::unicode_bom::UnicodeBom { } } impl RuleRunner for crate::rules::eslint::use_isnan::UseIsnan { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::BinaryExpression, + AstType::CallExpression, + AstType::SwitchCase, + AstType::SwitchStatement, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -1502,8 +1565,9 @@ impl RuleRunner for crate::rules::import::namespace::Namespace { } } impl RuleRunner for crate::rules::import::no_absolute_path::NoAbsolutePath { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::ImportDeclaration]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -1652,8 +1716,9 @@ impl RuleRunner for crate::rules::import::no_unassigned_import::NoUnassignedImpo } } impl RuleRunner for crate::rules::import::no_webpack_loader_syntax::NoWebpackLoaderSyntax { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::ImportDeclaration]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -1905,8 +1970,9 @@ impl RuleRunner for crate::rules::jest::no_test_prefixes::NoTestPrefixes { } } impl RuleRunner for crate::rules::jest::no_test_return_statement::NoTestReturnStatement { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::Function]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -2975,8 +3041,9 @@ impl RuleRunner for crate::rules::oxc::number_arg_out_of_range::NumberArgOutOfRa } } impl RuleRunner for crate::rules::oxc::only_used_in_recursion::OnlyUsedInRecursion { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::ArrowFunctionExpression, AstType::Function]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -3162,8 +3229,9 @@ impl RuleRunner for crate::rules::react::exhaustive_deps::ExhaustiveDeps { } } impl RuleRunner for crate::rules::react::forbid_elements::ForbidElements { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::JSXOpeningElement]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -3218,8 +3286,9 @@ impl RuleRunner for crate::rules::react::jsx_filename_extension::JsxFilenameExte } } impl RuleRunner for crate::rules::react::jsx_fragments::JsxFragments { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::JSXElement, AstType::JSXFragment]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -3310,8 +3379,9 @@ impl RuleRunner for crate::rules::react::jsx_props_no_spread_multi::JsxPropsNoSp } } impl RuleRunner for crate::rules::react::no_array_index_key::NoArrayIndexKey { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::JSXElement]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -3339,8 +3409,9 @@ impl RuleRunner for crate::rules::react::no_danger::NoDanger { } } impl RuleRunner for crate::rules::react::no_danger_with_children::NoDangerWithChildren { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::JSXElement]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -3440,8 +3511,9 @@ impl RuleRunner for crate::rules::react::prefer_es6_class::PreferEs6Class { } } impl RuleRunner for crate::rules::react::react_in_jsx_scope::ReactInJsxScope { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::JSXFragment, AstType::JSXOpeningElement]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -3625,8 +3697,12 @@ impl RuleRunner impl RuleRunner for crate::rules::typescript::consistent_type_definitions::ConsistentTypeDefinitions { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::ExportDefaultDeclaration, + AstType::TSInterfaceDeclaration, + AstType::TSTypeAliasDeclaration, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -3690,8 +3766,9 @@ impl RuleRunner for crate::rules::typescript::no_base_to_string::NoBaseToString impl RuleRunner for crate::rules::typescript::no_confusing_non_null_assertion::NoConfusingNonNullAssertion { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::AssignmentExpression, AstType::BinaryExpression]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -3749,8 +3826,9 @@ impl RuleRunner for crate::rules::typescript::no_empty_interface::NoEmptyInterfa } } impl RuleRunner for crate::rules::typescript::no_empty_object_type::NoEmptyObjectType { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::TSInterfaceDeclaration, AstType::TSTypeLiteral]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -4132,8 +4210,12 @@ impl RuleRunner for crate::rules::typescript::no_var_requires::NoVarRequires { } } impl RuleRunner for crate::rules::typescript::no_wrapper_object_types::NoWrapperObjectTypes { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::TSClassImplements, + AstType::TSInterfaceHeritage, + AstType::TSTypeReference, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -4370,8 +4452,9 @@ impl RuleRunner for crate::rules::typescript::use_unknown_in_catch_callback_vari } } impl RuleRunner for crate::rules::unicorn::catch_error_name::CatchErrorName { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::CatchParameter]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -4429,8 +4512,14 @@ impl RuleRunner for crate::rules::unicorn::consistent_function_scoping::Consiste } } impl RuleRunner for crate::rules::unicorn::empty_brace_spaces::EmptyBraceSpaces { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::BlockStatement, + AstType::Class, + AstType::FunctionBody, + AstType::ObjectExpression, + AstType::StaticBlock, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -4622,8 +4711,9 @@ impl RuleRunner for crate::rules::unicorn::no_instanceof_builtins::NoInstanceofB } } impl RuleRunner for crate::rules::unicorn::no_invalid_fetch_options::NoInvalidFetchOptions { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::NewExpression]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -4823,8 +4913,9 @@ impl RuleRunner for crate::rules::unicorn::no_unnecessary_slice_end::NoUnnecessa impl RuleRunner for crate::rules::unicorn::no_unreadable_array_destructuring::NoUnreadableArrayDestructuring { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::ArrayAssignmentTarget, AstType::ArrayPattern]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -4890,8 +4981,9 @@ impl RuleRunner for crate::rules::unicorn::no_useless_switch_case::NoUselessSwit } } impl RuleRunner for crate::rules::unicorn::no_useless_undefined::NoUselessUndefined { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::CallExpression, AstType::IdentifierReference]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = false; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -4908,8 +5000,9 @@ impl RuleRunner for crate::rules::unicorn::no_zero_fractions::NoZeroFractions { } } impl RuleRunner for crate::rules::unicorn::number_literal_case::NumberLiteralCase { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::BigIntLiteral, AstType::NumericLiteral]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -4937,8 +5030,13 @@ impl RuleRunner for crate::rules::unicorn::prefer_add_event_listener::PreferAddE } } impl RuleRunner for crate::rules::unicorn::prefer_array_find::PreferArrayFind { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::AssignmentExpression, + AstType::CallExpression, + AstType::ComputedMemberExpression, + AstType::VariableDeclarator, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -5103,8 +5201,12 @@ impl RuleRunner for crate::rules::unicorn::prefer_math_min_max::PreferMathMinMax } } impl RuleRunner for crate::rules::unicorn::prefer_math_trunc::PreferMathTrunc { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::from_types(&[ + AstType::AssignmentExpression, + AstType::BinaryExpression, + AstType::UnaryExpression, + ]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { @@ -5359,8 +5461,9 @@ impl RuleRunner for crate::rules::unicorn::switch_case_braces::SwitchCaseBraces impl RuleRunner for crate::rules::unicorn::text_encoding_identifier_case::TextEncodingIdentifierCase { - const NODE_TYPES: &AstTypesBitset = &AstTypesBitset::new(); - const ANY_NODE_TYPE: bool = true; + const NODE_TYPES: &AstTypesBitset = + &AstTypesBitset::from_types(&[AstType::JSXText, AstType::StringLiteral]); + const ANY_NODE_TYPE: bool = false; const ONLY_RUNS_ON_NODES: bool = true; #[inline] fn types_info(&self) -> (&'static AstTypesBitset, bool, bool) { diff --git a/tasks/linter_codegen/src/main.rs b/tasks/linter_codegen/src/main.rs index 5b8b3065f5d2b..274b4cedc5a17 100644 --- a/tasks/linter_codegen/src/main.rs +++ b/tasks/linter_codegen/src/main.rs @@ -248,18 +248,22 @@ impl IfElseKindDetector { if func.sig.ident != "run" { continue; } - // Only consider when the body has exactly one top-level statement and it's an `if`. - let block = &func.block; - if block.stmts.len() != 1 { - return None; - } - let stmt = &block.stmts[0]; - let Stmt::Expr(Expr::If(ifexpr), _) = stmt else { return None }; + // New behavior: the entire run body must be composed of only + // top-level `if let AstKind::... = node.kind()` chains. Aggregate + // variants across all these `if` statements. If any other kind of + // statement appears, we bail. let mut variants = BTreeSet::new(); - if !collect_if_chain_variants(ifexpr, &mut variants) { - return None; + let mut saw_if = false; + for stmt in &func.block.stmts { + let Stmt::Expr(Expr::If(ifexpr), _) = stmt else { return None }; + saw_if = true; + if !collect_if_chain_variants(ifexpr, &mut variants) { + return None; + } + } + if saw_if && !variants.is_empty() { + return Some(Self { variants }); } - return Some(Self { variants }); } } None @@ -344,6 +348,9 @@ struct MatchKindDetector { impl MatchKindDetector { fn from_file(file: &File) -> Option { + // Collect variants from any top-level `match node.kind()` expressions, + // including those embedded in a let-binding initializer. + let mut collected: BTreeSet = BTreeSet::new(); for item in &file.items { let syn::Item::Impl(imp) = item else { continue }; for impl_item in &imp.items { @@ -351,39 +358,42 @@ impl MatchKindDetector { if func.sig.ident != "run" { continue; } - let block = &func.block; - if block.stmts.len() != 1 { - return None; - } - let stmt = &block.stmts[0]; - let matchexpr: &ExprMatch = match stmt { - Stmt::Expr(Expr::Match(m), _) => m, - _ => return None, - }; - // Ensure the scrutinee is node.kind() - if !is_node_kind_call(&matchexpr.expr) { - return None; - } - // If any arm has a guard `if ...`, or the wildcard arm contains code, bail to ANY - for arm in &matchexpr.arms { - if arm.guard.is_some() { - return None; - } - if arm_pat_has_wildcard(&arm.pat) && arm_body_has_code(&arm.body) { - return None; + + // Special-case: If the run body contains exactly one statement and it is a + // bare `match node.kind()`, then allow guards in arms and include all explicit + // variants from that match. Otherwise, guards are disallowed. + if func.block.stmts.len() == 1 { + if let Stmt::Expr(Expr::Match(m), _) = &func.block.stmts[0] { + if let Some(vars) = extract_variants_from_top_level_match(m, /*allow_guards=*/true) { + collected.extend(vars); + } + // We handled the only statement; proceed to next impl item. + continue; } } - let mut variants = BTreeSet::new(); - for arm in &matchexpr.arms { - if arm_pat_has_wildcard(&arm.pat) { + + for stmt in &func.block.stmts { + // Case 1: bare `match node.kind()` as a top-level statement + if let Stmt::Expr(Expr::Match(m), _) = stmt { + if let Some(vars) = extract_variants_from_top_level_match(m, /*allow_guards=*/false) { + collected.extend(vars); + } continue; } - let _ = extract_variants_from_pat(&arm.pat, &mut variants); + // Case 2: `let ... = match node.kind() { .. };` + if let Stmt::Local(local) = stmt { + if let Some(init) = &local.init { + if let Expr::Match(m) = &*init.expr { + if let Some(vars) = extract_variants_from_top_level_match(m, /*allow_guards=*/false) { + collected.extend(vars); + } + } + } + } } - return Some(Self { variants }); } } - None + if collected.is_empty() { None } else { Some(Self { variants: collected }) } } } @@ -395,13 +405,35 @@ fn arm_pat_has_wildcard(pat: &Pat) -> bool { } } -fn arm_body_has_code(expr: &Expr) -> bool { +// Returns true if the expression is a plain `return` (diverging) or a block +// whose sole statement is a plain `return`. +fn arm_body_is_diverging_return_only(expr: &Expr) -> bool { match expr { - Expr::Block(b) => !b.block.stmts.is_empty(), - _ => true, + Expr::Return(ret) => ret.expr.is_none(), + Expr::Block(b) => { + let stmts = &b.block.stmts; + if stmts.len() != 1 { + return false; + } + match &stmts[0] { + Stmt::Expr(Expr::Return(ret), _) => ret.expr.is_none(), + _ => false, + } + } + _ => false, } } +// Returns true if the expression is a block with no statements +fn arm_body_is_empty_block(expr: &Expr) -> bool { + matches!(expr, Expr::Block(b) if b.block.stmts.is_empty()) +} + +// Returns true if the expression is the unit value `()`. +fn arm_body_is_unit(expr: &Expr) -> bool { + matches!(expr, Expr::Tuple(t) if t.elems.is_empty()) +} + fn else_expr_is_only_return(expr: &Expr) -> bool { if let Expr::Block(b) = expr { let stmts = &b.block.stmts; @@ -464,6 +496,63 @@ fn extract_variants_from_pat(pat: &Pat, out: &mut BTreeSet) -> bool { } } +// Given a top-level match expression, validate it's over `node.kind()` and that +// either there is no wildcard arm or the wildcard arm is a diverging `return`. +// If valid, extract and return the set of AstKind variants from non-wildcard arms. +fn extract_variants_from_top_level_match( + matchexpr: &ExprMatch, + allow_guards: bool, +) -> Option> { + // Ensure the scrutinee is node.kind() + if !is_node_kind_call(&matchexpr.expr) { + return None; + } + // If any arm has a guard `if ...`, bail + if !allow_guards { + for arm in &matchexpr.arms { + if arm.guard.is_some() { + return None; + } + } + } else { + // Guards are only allowed when the arm pattern itself is an AstKind pattern. + // Disallow guarded arms like `pat_ident if ...` that are not matching AstKind. + for arm in &matchexpr.arms { + if arm.guard.is_some() && !pat_is_astkind(&arm.pat) { + return None; + } + } + } + // Check wildcard arm behavior + if let Some(wild) = matchexpr.arms.iter().find(|a| arm_pat_has_wildcard(&a.pat)) { + // Allow empty block (no-op), unit `()`, or a diverging `return;` only + if !(arm_body_is_empty_block(&wild.body) + || arm_body_is_unit(&wild.body) + || arm_body_is_diverging_return_only(&wild.body)) + { + return None; + } + } + let mut variants = BTreeSet::new(); + for arm in &matchexpr.arms { + if arm_pat_has_wildcard(&arm.pat) { + continue; + } + let _ = extract_variants_from_pat(&arm.pat, &mut variants); + } + if variants.is_empty() { None } else { Some(variants) } +} + +// Returns true if the pattern is of the form `AstKind::Variant(..)` or `AstKind::Variant`. +fn pat_is_astkind(pat: &Pat) -> bool { + match pat { + Pat::TupleStruct(ts) => astkind_variant_from_path(&ts.path).is_some(), + Pat::Path(ppath) => astkind_variant_from_path(&ppath.path).is_some(), + Pat::Or(orpat) => orpat.cases.iter().all(pat_is_astkind), + _ => false, + } +} + fn astkind_variant_from_path(path: &SynPath) -> Option { // Expect `AstKind::Variant` let mut segments = path.segments.iter(); From 16221c023d50d001ebbb34d0145c85c875229c71 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 02:50:40 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- tasks/linter_codegen/src/main.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tasks/linter_codegen/src/main.rs b/tasks/linter_codegen/src/main.rs index 274b4cedc5a17..fe11d8026e09f 100644 --- a/tasks/linter_codegen/src/main.rs +++ b/tasks/linter_codegen/src/main.rs @@ -364,7 +364,9 @@ impl MatchKindDetector { // variants from that match. Otherwise, guards are disallowed. if func.block.stmts.len() == 1 { if let Stmt::Expr(Expr::Match(m), _) = &func.block.stmts[0] { - if let Some(vars) = extract_variants_from_top_level_match(m, /*allow_guards=*/true) { + if let Some(vars) = + extract_variants_from_top_level_match(m, /*allow_guards=*/ true) + { collected.extend(vars); } // We handled the only statement; proceed to next impl item. @@ -375,7 +377,9 @@ impl MatchKindDetector { for stmt in &func.block.stmts { // Case 1: bare `match node.kind()` as a top-level statement if let Stmt::Expr(Expr::Match(m), _) = stmt { - if let Some(vars) = extract_variants_from_top_level_match(m, /*allow_guards=*/false) { + if let Some(vars) = + extract_variants_from_top_level_match(m, /*allow_guards=*/ false) + { collected.extend(vars); } continue; @@ -384,7 +388,9 @@ impl MatchKindDetector { if let Stmt::Local(local) = stmt { if let Some(init) = &local.init { if let Expr::Match(m) = &*init.expr { - if let Some(vars) = extract_variants_from_top_level_match(m, /*allow_guards=*/false) { + if let Some(vars) = extract_variants_from_top_level_match( + m, /*allow_guards=*/ false, + ) { collected.extend(vars); } }