Skip to content

Commit

Permalink
feat(linter) improve prefer-string-starts-ends-with rule (#3176)
Browse files Browse the repository at this point in the history
basically:
`^#/i.test(hex)` is the same as `/^#/.test(hex)`
so, in this case the `i` flag does nothing and we can safely ignore it


inspired by https://x.com/the_moisrex/status/1787444601571221892


---

This could potentially lead a new `oxc` rule called
`no_useless_case_insensitive_regex_flag` that reports if you have a
regex with the `i` flag with no ascii alphabetic chars.
  • Loading branch information
camc314 authored May 6, 2024
1 parent cb2e651 commit 07076d9
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ enum ErrorKind {
}

fn check_regex(regexp_lit: &RegExpLiteral) -> Option<ErrorKind> {
if regexp_lit.regex.flags.intersects(RegExpFlags::I | RegExpFlags::M) {
if regexp_lit.regex.flags.intersects(RegExpFlags::M)
|| (regexp_lit.regex.flags.intersects(RegExpFlags::I | RegExpFlags::M)
&& is_useless_case_sensitive_regex_flag(regexp_lit))
{
return None;
}

Expand All @@ -118,6 +121,14 @@ fn is_simple_string(str: &str) -> bool {
.all(|c| !matches!(c, '^' | '$' | '+' | '[' | '{' | '(' | '\\' | '.' | '?' | '*' | '|'))
}

// `/^#/i` => `true` (the `i` flag is useless)
// `/^foo/i` => `false` (the `i` flag is not useless)
fn is_useless_case_sensitive_regex_flag(regexp_lit: &RegExpLiteral) -> bool {
// ignore `^` and `$` (start and end of string)
let pat = regexp_lit.regex.pattern.trim_start_matches('^').trim_end_matches('$');
pat.chars().all(|c| c.is_ascii_alphabetic())
}

#[test]
fn test() {
use crate::tester::Tester;
Expand Down Expand Up @@ -152,6 +163,8 @@ fn test() {
r"/A|B$/.test(bar)",
// Additional tests
r"/^http/i.test(uri)",
r"if (/^a/i.test(hex)) {}",
r"if (/a$/i.test(hex)) {}",
];

let fail = vec![
Expand Down Expand Up @@ -196,6 +209,8 @@ fn test() {
r"/a$/.test(String(unknown))",
r"const a = /你$/.test('a');",
r"const a = /^你/.test('a');",
r"if (/^#/i.test(hex)) {}",
r"if (/#$/i.test(hex)) {}",
];

Tester::new(PreferStringStartsEndsWith::NAME, pass, fail).test_and_snapshot();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,15 @@ expression: prefer_string_starts_ends_with
1const a = /^/.test('a');
· ──────────
╰────

eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
╭─[prefer_string_starts_ends_with.tsx:1:5]
1if (/^#/i.test(hex)) {}
· ──────────
╰────

eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
╭─[prefer_string_starts_ends_with.tsx:1:5]
1if (/#$/i.test(hex)) {}
· ──────────
╰────

0 comments on commit 07076d9

Please sign in to comment.