diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index 7329e2e5facc8..76e5db27c57f5 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -285,7 +285,11 @@ impl RuleRunner for crate::rules::eslint::no_continue::NoContinue { } impl RuleRunner for crate::rules::eslint::no_control_regex::NoControlRegex { - const NODE_TYPES: Option<&AstTypesBitset> = None; + const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[ + AstType::CallExpression, + AstType::NewExpression, + AstType::RegExpLiteral, + ])); const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; } @@ -506,7 +510,11 @@ impl RuleRunner for crate::rules::eslint::no_magic_numbers::NoMagicNumbers { impl RuleRunner for crate::rules::eslint::no_misleading_character_class::NoMisleadingCharacterClass { - const NODE_TYPES: Option<&AstTypesBitset> = None; + const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[ + AstType::CallExpression, + AstType::NewExpression, + AstType::RegExpLiteral, + ])); const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; } @@ -804,7 +812,11 @@ impl RuleRunner for crate::rules::eslint::no_unused_vars::NoUnusedVars { } impl RuleRunner for crate::rules::eslint::no_useless_backreference::NoUselessBackreference { - const NODE_TYPES: Option<&AstTypesBitset> = None; + const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[ + AstType::CallExpression, + AstType::NewExpression, + AstType::RegExpLiteral, + ])); const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; } diff --git a/tasks/linter_codegen/src/main.rs b/tasks/linter_codegen/src/main.rs index 4dfffe1f64983..3dd5799dcd5e9 100644 --- a/tasks/linter_codegen/src/main.rs +++ b/tasks/linter_codegen/src/main.rs @@ -25,6 +25,7 @@ mod let_else_detector; mod match_detector; mod member_expression_kinds; mod node_type_set; +mod regex_node_kinds; mod rules; mod utils; @@ -45,7 +46,9 @@ pub fn generate_rule_runner_impls() -> io::Result<()> { let member_expression_kinds = get_member_expression_kinds().expect("Failed to get member expression kinds"); - let rule_runner_data = RuleRunnerData { member_expression_kinds }; + let regex_node_kinds = + regex_node_kinds::get_regex_node_kinds().expect("Failed to get regex node kinds"); + let rule_runner_data = RuleRunnerData { member_expression_kinds, regex_node_kinds }; let mut out = String::new(); out.push_str("// Auto-generated code, DO NOT EDIT DIRECTLY!\n"); @@ -152,6 +155,16 @@ fn detect_top_level_node_types( return Some(node_types); } + // Detect if entire body is call to `run_on_regex_node` and return those node types + if run_func.block.stmts.len() == 1 + && let syn::Stmt::Expr(syn::Expr::Call(call_expr), _) = &run_func.block.stmts[0] + && call_expr.args.len() == 3 + && let syn::Expr::Path(path_expr) = &*call_expr.func + && path_expr.path.is_ident("run_on_regex_node") + { + return Some(rule_runner_data.regex_node_kinds.clone()); + } + None } @@ -199,6 +212,7 @@ enum CollectionResult { /// Additional data collected for rule runner impl generation struct RuleRunnerData { member_expression_kinds: NodeTypeSet, + regex_node_kinds: NodeTypeSet, } /// Format Rust code with `rustfmt`. diff --git a/tasks/linter_codegen/src/regex_node_kinds.rs b/tasks/linter_codegen/src/regex_node_kinds.rs new file mode 100644 index 0000000000000..a16ce12e981f2 --- /dev/null +++ b/tasks/linter_codegen/src/regex_node_kinds.rs @@ -0,0 +1,42 @@ +use syn::{Expr, Pat, Stmt}; + +use crate::{node_type_set::NodeTypeSet, utils::astkind_variant_from_path}; + +/// Fetches the current list of variants that are handled by `run_on_regex_node`. +/// We read the source file to avoid hardcoding the list here and ensure this will stay updated. +pub fn get_regex_node_kinds() -> Option { + // Read crates/oxc_linter/src/utils/regex.rs and extract all variants in `run_on_regex_node` function + let regex_utils_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .parent()? + .parent()? + .join("crates") + .join("oxc_linter") + .join("src") + .join("utils") + .join("regex.rs"); + let content = std::fs::read_to_string(regex_utils_path).ok()?; + let syntax = syn::parse_file(&content).ok()?; + let mut node_type_set = NodeTypeSet::new(); + for item in syntax.items { + if let syn::Item::Fn(func) = item + && func.sig.ident == "run_on_regex_node" + { + // Look for `match node.kind() { ... }` inside the function body + for stmt in &func.block.stmts { + if let Stmt::Expr(Expr::Match(match_expr), _) = stmt { + for arm in &match_expr.arms { + if let Pat::TupleStruct(ts) = &arm.pat + && let Some(variant) = astkind_variant_from_path(&ts.path) + { + node_type_set.insert(variant); + } + } + if !node_type_set.is_empty() { + return Some(node_type_set); + } + } + } + } + } + None +}