diff --git a/crates/oxc_linter/src/rules/eslint/no_control_regex.rs b/crates/oxc_linter/src/rules/eslint/no_control_regex.rs index 0e0b0bba20028..20bc9442388ed 100644 --- a/crates/oxc_linter/src/rules/eslint/no_control_regex.rs +++ b/crates/oxc_linter/src/rules/eslint/no_control_regex.rs @@ -12,10 +12,15 @@ use oxc_span::{GetSpan, Span}; use crate::{ast_util::extract_regex_flags, context::LintContext, rule::Rule, AstNode}; -fn no_control_regex_diagnostic(regex: &str, span: Span) -> OxcDiagnostic { - OxcDiagnostic::warn("Unexpected control character(s)") - .with_help(format!("Unexpected control character(s) in regular expression: \"{regex}\"")) - .with_label(span) +fn no_control_regex_diagnostic(count: usize, regex: &str, span: Span) -> OxcDiagnostic { + debug_assert!(count > 0); + let (message, help) = if count == 1 { + ("Unexpected control character", format!("'{regex}' is not a valid control character.")) + } else { + ("Unexpected control characters", format!("'{regex}' are not valid control characters.")) + }; + + OxcDiagnostic::warn(message).with_help(help).with_label(span) } #[derive(Debug, Default, Clone)] @@ -89,7 +94,12 @@ impl Rule for NoControlRegex { if let Argument::StringLiteral(pattern) = &expr.arguments[0] { // get pattern from arguments. Missing or non-string arguments // will be runtime errors, but are not covered by this rule. - parse_and_check_regex(context, &pattern.value, &expr.arguments, expr.span); + parse_and_check_regex( + context, + &pattern.value, + &expr.arguments, + pattern.span, + ); } } } @@ -107,7 +117,12 @@ impl Rule for NoControlRegex { if let Argument::StringLiteral(pattern) = &expr.arguments[0] { // get pattern from arguments. Missing or non-string arguments // will be runtime errors, but are not covered by this rule. - parse_and_check_regex(context, &pattern.value, &expr.arguments, expr.span); + parse_and_check_regex( + context, + &pattern.value, + &expr.arguments, + pattern.span, + ); } } } @@ -143,8 +158,9 @@ fn check_pattern(context: &LintContext, pattern: &Pattern, span: Span) { finder.visit_pattern(pattern); if !finder.control_chars.is_empty() { + let num_control_chars = finder.control_chars.len(); let violations = finder.control_chars.into_iter().map(|c| c.to_string()).join(", "); - context.diagnostic(no_control_regex_diagnostic(&violations, span)); + context.diagnostic(no_control_regex_diagnostic(num_control_chars, &violations, span)); } } diff --git a/crates/oxc_linter/src/snapshots/no_control_regex.snap b/crates/oxc_linter/src/snapshots/no_control_regex.snap index f931a8ab5d353..c7241ca0d72c1 100644 Binary files a/crates/oxc_linter/src/snapshots/no_control_regex.snap and b/crates/oxc_linter/src/snapshots/no_control_regex.snap differ diff --git a/crates/oxc_linter/src/snapshots/no_control_regex@capture-group-indexing.snap b/crates/oxc_linter/src/snapshots/no_control_regex@capture-group-indexing.snap index 3424f21195c02..e571967fdda45 100644 --- a/crates/oxc_linter/src/snapshots/no_control_regex@capture-group-indexing.snap +++ b/crates/oxc_linter/src/snapshots/no_control_regex@capture-group-indexing.snap @@ -1,30 +1,30 @@ --- source: crates/oxc_linter/src/tester.rs --- - ⚠ eslint(no-control-regex): Unexpected control character(s) + ⚠ eslint(no-control-regex): Unexpected control character ╭─[no_control_regex.tsx:1:11] 1 │ const r = /\0/; · ──── ╰──── - help: Unexpected control character(s) in regular expression: "\0" + help: '\0' is not a valid control character. - ⚠ eslint(no-control-regex): Unexpected control character(s) + ⚠ eslint(no-control-regex): Unexpected control character ╭─[no_control_regex.tsx:1:11] 1 │ const r = /[a-z]\1/; · ───────── ╰──── - help: Unexpected control character(s) in regular expression: "\1" + help: '\1' is not a valid control character. - ⚠ eslint(no-control-regex): Unexpected control character(s) + ⚠ eslint(no-control-regex): Unexpected control character ╭─[no_control_regex.tsx:1:11] 1 │ const r = /([a-z])\2/; · ─────────── ╰──── - help: Unexpected control character(s) in regular expression: "\2" + help: '\2' is not a valid control character. - ⚠ eslint(no-control-regex): Unexpected control character(s) + ⚠ eslint(no-control-regex): Unexpected control character ╭─[no_control_regex.tsx:1:11] 1 │ const r = /([a-z])\0/; · ─────────── ╰──── - help: Unexpected control character(s) in regular expression: "\0" + help: '\0' is not a valid control character.