From 070ae53ad60237ee9c4d31adb5bc463989efbd66 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Sat, 10 Aug 2024 14:46:12 +0000 Subject: [PATCH] feat(linter): add fixer for unicorn prefer-string-replace-all (#4801) --- .../unicorn/prefer_string_replace_all.rs | 22 ++++++-- .../snapshots/prefer_string_replace_all.snap | 51 ++++++++++++++++--- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs b/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs index 59d4307d5..21e3d27a4 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_string_replace_all.rs @@ -6,6 +6,8 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, Span}; +use oxc_span::GetSpan; + use crate::{ast_util::extract_regex_flags, context::LintContext, rule::Rule, AstNode}; fn string_literal(span0: Span, x1: &str) -> OxcDiagnostic { @@ -45,7 +47,8 @@ declare_oxc_lint!( /// foo.replace(pattern, bar) /// ``` PreferStringReplaceAll, - pedantic + pedantic, + fix ); impl Rule for PreferStringReplaceAll { @@ -76,11 +79,17 @@ impl Rule for PreferStringReplaceAll { match method_name_str { "replaceAll" => { if let Some(k) = get_pattern_replacement(pattern) { - ctx.diagnostic(string_literal(static_member_expr.property.span, &k)); + ctx.diagnostic_with_fix(string_literal(pattern.span(), &k), |fixer| { + // foo.replaceAll(/hello world/g, bar) => foo.replaceAll("hello world", bar) + fixer.replace(pattern.span(), format!("{k:?}")) + }); } } "replace" if is_reg_exp_with_global_flag(pattern) => { - ctx.diagnostic(use_replace_all(static_member_expr.property.span)); + ctx.diagnostic_with_fix( + use_replace_all(static_member_expr.property.span), + |fixer| fixer.replace(static_member_expr.property.span, "replaceAll"), + ); } _ => {} } @@ -213,5 +222,10 @@ fn test() { r#""Hello world".replaceAll(/world/g, 'world!');"#, ]; - Tester::new(PreferStringReplaceAll::NAME, pass, fail).test_and_snapshot(); + let fix = vec![ + ("foo.replace(/a/g, bar)", "foo.replaceAll(/a/g, bar)"), + ("foo.replaceAll(/a/g, bar)", "foo.replaceAll(\"a\", bar)"), + ]; + + Tester::new(PreferStringReplaceAll::NAME, pass, fail).expect_fix(fix).test_and_snapshot(); } diff --git a/crates/oxc_linter/src/snapshots/prefer_string_replace_all.snap b/crates/oxc_linter/src/snapshots/prefer_string_replace_all.snap index f76aaef02..a5602f380 100644 --- a/crates/oxc_linter/src/snapshots/prefer_string_replace_all.snap +++ b/crates/oxc_linter/src/snapshots/prefer_string_replace_all.snap @@ -6,231 +6,270 @@ source: crates/oxc_linter/src/tester.rs 1 │ foo.replace(/a/g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/"'/g, '\'') · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\./g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\\\./g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\|/g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a/gu, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a/ug, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/[a]/g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a?/g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/.*/g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a|b/g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\W/g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\u{61}/g, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\u{61}/gu, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\u{61}/gv, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/]/g, "bar") · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a/gi, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a/gui, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a/uig, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a/vig, bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(new RegExp("foo", "g"), bar) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a]/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/[a]/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a{1/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/a{1}/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\u0022/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\u0027/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\cM\cj/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\x22/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\x27/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\uD83D\ude00/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\u{1f600}/gu, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\n/g, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\u{20}/gu, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/\u{20}/gv, _) · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): This pattern can be replaced with `a]`. - ╭─[prefer_string_replace_all.tsx:1:5] + ╭─[prefer_string_replace_all.tsx:1:16] 1 │ foo.replaceAll(/a]/g, _) - · ────────── + · ───── ╰──── + help: Replace `/a]/g` with `"a]"`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): This pattern can be replaced with `a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string`. - ╭─[prefer_string_replace_all.tsx:1:5] + ╭─[prefer_string_replace_all.tsx:1:16] 1 │ foo.replaceAll(/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g, _) - · ────────── + · ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ╰──── + help: Replace `/a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string/g` with `"a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string"`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): Prefer `String#replaceAll()` over `String#replace()` when using a regex with the global flag. ╭─[prefer_string_replace_all.tsx:1:5] 1 │ foo.replace(/(?!a)+/g, "") · ─────── ╰──── + help: Replace `replace` with `replaceAll`. ⚠ eslint-plugin-unicorn(prefer-string-replace-all): This pattern can be replaced with `world`. - ╭─[prefer_string_replace_all.tsx:1:15] + ╭─[prefer_string_replace_all.tsx:1:26] 1 │ "Hello world".replaceAll(/world/g, 'world!'); - · ────────── + · ──────── ╰──── + help: Replace `/world/g` with `"world"`.