diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 6404168f8..dc504c900 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -52,6 +52,7 @@ mod eslint { pub mod no_control_regex; pub mod no_debugger; pub mod no_delete_var; + pub mod no_div_regex; pub mod no_dupe_class_members; pub mod no_dupe_else_if; pub mod no_dupe_keys; @@ -428,6 +429,7 @@ oxc_macros::declare_all_lint_rules! { eslint::no_control_regex, eslint::no_debugger, eslint::no_delete_var, + eslint::no_div_regex, eslint::no_dupe_class_members, eslint::no_dupe_else_if, eslint::no_dupe_keys, diff --git a/crates/oxc_linter/src/rules/eslint/no_div_regex.rs b/crates/oxc_linter/src/rules/eslint/no_div_regex.rs new file mode 100644 index 000000000..13a0cac03 --- /dev/null +++ b/crates/oxc_linter/src/rules/eslint/no_div_regex.rs @@ -0,0 +1,67 @@ +use oxc_ast::AstKind; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, fixer::Fix, rule::Rule, AstNode}; + +fn no_div_regex_diagnostic(span0: Span) -> OxcDiagnostic { + OxcDiagnostic::warn( + "eslint(no-div-regex): A regular expression literal can be confused with '/='.", + ) + .with_help("Rewrite `/=` into `/[=]`") + .with_labels([span0.into()]) +} + +#[derive(Debug, Default, Clone)] +pub struct NoDivRegex; + +declare_oxc_lint!( + /// ### What it does + /// + /// Disallow equal signs explicitly at the beginning of regular expressions. + /// + /// ### Why is this bad? + /// + /// Characters /= at the beginning of a regular expression literal can be confused with a + /// division assignment operator. + /// + /// ### Example + /// ```javascript + /// function bar() { return /=foo/; } + /// ``` + NoDivRegex, + restriction, +); + +impl Rule for NoDivRegex { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::RegExpLiteral(lit) = node.kind() { + if lit.regex.pattern.starts_with('=') { + ctx.diagnostic_with_fix(no_div_regex_diagnostic(lit.span), || { + Fix::new("[=]", Span::new(lit.span.start + 1, lit.span.start + 2)) + }); + } + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "var f = function() { return /foo/ig.test('bar'); };", + "var f = function() { return /\\=foo/; };", + ]; + + let fail = vec!["var f = function() { return /=foo/; };"]; + + let fix = vec![( + "var f = function() { return /=foo/; };", + "var f = function() { return /[=]foo/; };", + None, + )]; + + Tester::new(NoDivRegex::NAME, pass, fail).expect_fix(fix).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_div_regex.snap b/crates/oxc_linter/src/snapshots/no_div_regex.snap new file mode 100644 index 000000000..435f7f65f --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_div_regex.snap @@ -0,0 +1,10 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: no_div_regex +--- + ⚠ eslint(no-div-regex): A regular expression literal can be confused with '/='. + ╭─[no_div_regex.tsx:1:29] + 1 │ var f = function() { return /=foo/; }; + · ────── + ╰──── + help: Rewrite `/=` into `/[=]`