Skip to content

Commit e67901b

Browse files
committed
fix(linter): incorrect fix for prefer start ends with (#10533)
fixes #10523
1 parent 7d5ad7d commit e67901b

File tree

2 files changed

+54
-40
lines changed

2 files changed

+54
-40
lines changed

crates/oxc_linter/src/rules/unicorn/prefer_string_starts_ends_with.rs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use oxc_allocator::Allocator;
12
use oxc_ast::{
2-
AstKind,
3+
AstBuilder, AstKind,
34
ast::{CallExpression, Expression, MemberExpression, RegExpFlags, RegExpLiteral},
45
};
56
use oxc_diagnostics::OxcDiagnostic;
67
use oxc_macros::declare_oxc_lint;
78
use oxc_regular_expression::ast::{BoundaryAssertionKind, Term};
8-
use oxc_span::{GetSpan, Span};
9+
use oxc_span::{GetSpan, SPAN, Span};
910

1011
use crate::{
1112
AstNode,
@@ -116,10 +117,15 @@ fn do_fix<'a>(
116117
}
117118
};
118119
let Some(argument) = argument else { return fixer.noop() };
119-
let fix_text = format!(r#"{}.{}("{}")"#, fixer.source_range(target_span), method, argument);
120-
121-
fixer.replace(call_expr.span, fix_text)
120+
let mut content = fixer.codegen();
121+
let alloc = Allocator::default();
122+
let ast = AstBuilder::new(&alloc);
123+
content.print_str(&format!(r"{}.{}(", fixer.source_range(target_span), method));
124+
content.print_expression(&ast.expression_string_literal(SPAN, argument, None));
125+
content.print_str(r")");
126+
fixer.replace(call_expr.span, content)
122127
}
128+
123129
fn can_replace(call_expr: &CallExpression) -> Option<Span> {
124130
if call_expr.arguments.len() != 1 {
125131
return None;
@@ -279,16 +285,24 @@ fn test() {
279285
];
280286

281287
let fix = vec![
282-
("/^foo/.test(x)", r#"x.startsWith("foo")"#, None),
283-
("/foo$/.test(x)", r#"x.endsWith("foo")"#, None),
284-
("/^foo/.test(x.y)", r#"x.y.startsWith("foo")"#, None),
285-
("/foo$/.test(x.y)", r#"x.y.endsWith("foo")"#, None),
286-
("/^foo/.test('x')", r#"'x'.startsWith("foo")"#, None),
287-
("/foo$/.test('x')", r#"'x'.endsWith("foo")"#, None),
288-
("/^foo/.test(`x${y}`)", r#"`x${y}`.startsWith("foo")"#, None),
289-
("/foo$/.test(`x${y}`)", r#"`x${y}`.endsWith("foo")"#, None),
290-
("/^foo/.test(String(x))", r#"String(x).startsWith("foo")"#, None),
291-
("/foo$/.test(String(x))", r#"String(x).endsWith("foo")"#, None),
288+
("/^foo/.test(x)", r"x.startsWith('foo')", None),
289+
("/foo$/.test(x)", r"x.endsWith('foo')", None),
290+
("/^foo/.test(x.y)", r"x.y.startsWith('foo')", None),
291+
("/foo$/.test(x.y)", r"x.y.endsWith('foo')", None),
292+
("/^foo/.test('x')", r"'x'.startsWith('foo')", None),
293+
("/foo$/.test('x')", r"'x'.endsWith('foo')", None),
294+
("/^foo/.test(`x${y}`)", r"`x${y}`.startsWith('foo')", None),
295+
("/foo$/.test(`x${y}`)", r"`x${y}`.endsWith('foo')", None),
296+
("/^foo/.test(String(x))", r"String(x).startsWith('foo')", None),
297+
("/foo$/.test(String(x))", r"String(x).endsWith('foo')", None),
298+
// https://github.com/oxc-project/oxc/issues/10523
299+
(
300+
r"const makePosix = str => /^\\\\\?\\/.test(str)",
301+
r"const makePosix = str => str.startsWith('\\\\?\\')",
302+
None,
303+
),
304+
("/^'/.test('foo')", r"'foo'.startsWith('\'')", None),
305+
(r#"/^"/.test('foo')"#, r#"'foo'.startsWith('"')"#, None),
292306
// should not get fixed
293307
("/^foo/.test(new String('bar'))", "/^foo/.test(new String('bar'))", None),
294308
("/^foo/.test(x as string)", "/^foo/.test(x as string)", None),

crates/oxc_linter/src/snapshots/unicorn_prefer_string_starts_ends_with.snap

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,91 +6,91 @@ source: crates/oxc_linter/src/tester.rs
66
1/^foo/.test(bar)
77
· ───────────
88
╰────
9-
help: Replace `/^foo/.test(bar)` with `bar.startsWith("foo")`.
9+
help: Replace `/^foo/.test(bar)` with `bar.startsWith('foo')`.
1010

1111
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
1212
╭─[prefer_string_starts_ends_with.tsx:1:1]
1313
1/foo$/.test(bar)
1414
· ───────────
1515
╰────
16-
help: Replace `/foo$/.test(bar)` with `bar.endsWith("foo")`.
16+
help: Replace `/foo$/.test(bar)` with `bar.endsWith('foo')`.
1717

1818
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
1919
╭─[prefer_string_starts_ends_with.tsx:1:1]
2020
1/\$$/.test(bar)
2121
· ──────────
2222
╰────
23-
help: Replace `/\$$/.test(bar)` with `bar.endsWith("$")`.
23+
help: Replace `/\$$/.test(bar)` with `bar.endsWith('$')`.
2424

2525
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
2626
╭─[prefer_string_starts_ends_with.tsx:1:1]
2727
1/^\^/.test(bar)
2828
· ──────────
2929
╰────
30-
help: Replace `/^\^/.test(bar)` with `bar.startsWith("^")`.
30+
help: Replace `/^\^/.test(bar)` with `bar.startsWith('^')`.
3131

3232
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
3333
╭─[prefer_string_starts_ends_with.tsx:1:1]
3434
1/^!/.test(bar)
3535
· ─────────
3636
╰────
37-
help: Replace `/^!/.test(bar)` with `bar.startsWith("!")`.
37+
help: Replace `/^!/.test(bar)` with `bar.startsWith('!')`.
3838

3939
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
4040
╭─[prefer_string_starts_ends_with.tsx:1:1]
4141
1/!$/.test(bar)
4242
· ─────────
4343
╰────
44-
help: Replace `/!$/.test(bar)` with `bar.endsWith("!")`.
44+
help: Replace `/!$/.test(bar)` with `bar.endsWith('!')`.
4545

4646
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
4747
╭─[prefer_string_starts_ends_with.tsx:1:1]
4848
1/^ /.test(bar)
4949
· ─────────
5050
╰────
51-
help: Replace `/^ /.test(bar)` with `bar.startsWith(" ")`.
51+
help: Replace `/^ /.test(bar)` with `bar.startsWith(' ')`.
5252

5353
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
5454
╭─[prefer_string_starts_ends_with.tsx:1:1]
5555
1/ $/.test(bar)
5656
· ─────────
5757
╰────
58-
help: Replace `/ $/.test(bar)` with `bar.endsWith(" ")`.
58+
help: Replace `/ $/.test(bar)` with `bar.endsWith(' ')`.
5959

6060
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
6161
╭─[prefer_string_starts_ends_with.tsx:1:17]
6262
1const foo = {}; /^abc/.test(foo);
6363
· ───────────
6464
╰────
65-
help: Replace `/^abc/.test(foo)` with `foo.startsWith("abc")`.
65+
help: Replace `/^abc/.test(foo)` with `foo.startsWith('abc')`.
6666

6767
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
6868
╭─[prefer_string_starts_ends_with.tsx:1:18]
6969
1const foo = 123; /^abc/.test(foo);
7070
· ───────────
7171
╰────
72-
help: Replace `/^abc/.test(foo)` with `foo.startsWith("abc")`.
72+
help: Replace `/^abc/.test(foo)` with `foo.startsWith('abc')`.
7373

7474
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
7575
╭─[prefer_string_starts_ends_with.tsx:1:22]
7676
1const foo = "hello"; /^abc/.test(foo);
7777
· ───────────
7878
╰────
79-
help: Replace `/^abc/.test(foo)` with `foo.startsWith("abc")`.
79+
help: Replace `/^abc/.test(foo)` with `foo.startsWith('abc')`.
8080

8181
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
8282
╭─[prefer_string_starts_ends_with.tsx:1:1]
8383
1/^b/.test((a))
8484
· ─────────
8585
╰────
86-
help: Replace `/^b/.test((a))` with `a.startsWith("b")`.
86+
help: Replace `/^b/.test((a))` with `a.startsWith('b')`.
8787

8888
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
8989
╭─[prefer_string_starts_ends_with.tsx:1:1]
9090
1 │ (/^b/).test((a))
9191
· ───────────
9292
╰────
93-
help: Replace `(/^b/).test((a))` with `a.startsWith("b")`.
93+
help: Replace `(/^b/).test((a))` with `a.startsWith('b')`.
9494

9595
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
9696
╭─[prefer_string_starts_ends_with.tsx:1:24]
@@ -109,7 +109,7 @@ source: crates/oxc_linter/src/tester.rs
109109
1/^a/.test("string")
110110
· ─────────
111111
╰────
112-
help: Replace `/^a/.test("string")` with `"string".startsWith("a")`.
112+
help: Replace `/^a/.test("string")` with `"string".startsWith('a')`.
113113

114114
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
115115
╭─[prefer_string_starts_ends_with.tsx:1:1]
@@ -176,14 +176,14 @@ source: crates/oxc_linter/src/tester.rs
176176
1/^a/.test(foo.bar)
177177
· ─────────
178178
╰────
179-
help: Replace `/^a/.test(foo.bar)` with `foo.bar.startsWith("a")`.
179+
help: Replace `/^a/.test(foo.bar)` with `foo.bar.startsWith('a')`.
180180

181181
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
182182
╭─[prefer_string_starts_ends_with.tsx:1:1]
183183
1/^a/.test(foo.bar())
184184
· ─────────
185185
╰────
186-
help: Replace `/^a/.test(foo.bar())` with `foo.bar().startsWith("a")`.
186+
help: Replace `/^a/.test(foo.bar())` with `foo.bar().startsWith('a')`.
187187

188188
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
189189
╭─[prefer_string_starts_ends_with.tsx:1:1]
@@ -202,7 +202,7 @@ source: crates/oxc_linter/src/tester.rs
202202
1/^a/.test(`string`)
203203
· ─────────
204204
╰────
205-
help: Replace `/^a/.test(`string`)` with ``string`.startsWith("a")`.
205+
help: Replace `/^a/.test(`string`)` with ``string`.startsWith('a')`.
206206

207207
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
208208
╭─[prefer_string_starts_ends_with.tsx:1:1]
@@ -245,53 +245,53 @@ source: crates/oxc_linter/src/tester.rs
245245
1/^a/u.test("string")
246246
· ──────────
247247
╰────
248-
help: Replace `/^a/u.test("string")` with `"string".startsWith("a")`.
248+
help: Replace `/^a/u.test("string")` with `"string".startsWith('a')`.
249249

250250
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
251251
╭─[prefer_string_starts_ends_with.tsx:1:1]
252252
1/^a/v.test("string")
253253
· ──────────
254254
╰────
255-
help: Replace `/^a/v.test("string")` with `"string".startsWith("a")`.
255+
help: Replace `/^a/v.test("string")` with `"string".startsWith('a')`.
256256

257257
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
258258
╭─[prefer_string_starts_ends_with.tsx:1:1]
259259
1/a$/.test(`${unknown}`)
260260
· ─────────
261261
╰────
262-
help: Replace `/a$/.test(`${unknown}`)` with ``${unknown}`.endsWith("a")`.
262+
help: Replace `/a$/.test(`${unknown}`)` with ``${unknown}`.endsWith('a')`.
263263

264264
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
265265
╭─[prefer_string_starts_ends_with.tsx:1:1]
266266
1/a$/.test(String(unknown))
267267
· ─────────
268268
╰────
269-
help: Replace `/a$/.test(String(unknown))` with `String(unknown).endsWith("a")`.
269+
help: Replace `/a$/.test(String(unknown))` with `String(unknown).endsWith('a')`.
270270

271271
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
272272
╭─[prefer_string_starts_ends_with.tsx:1:11]
273273
1const a = /$/.test('a');
274274
· ──────────
275275
╰────
276-
help: Replace `/你$/.test('a')` with `'a'.endsWith("你")`.
276+
help: Replace `/你$/.test('a')` with `'a'.endsWith('你')`.
277277

278278
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
279279
╭─[prefer_string_starts_ends_with.tsx:1:11]
280280
1const a = /^/.test('a');
281281
· ──────────
282282
╰────
283-
help: Replace `/^你/.test('a')` with `'a'.startsWith("你")`.
283+
help: Replace `/^你/.test('a')` with `'a'.startsWith('你')`.
284284

285285
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
286286
╭─[prefer_string_starts_ends_with.tsx:1:5]
287287
1if (/^#/i.test(hex)) {}
288288
· ──────────
289289
╰────
290-
help: Replace `/^#/i.test(hex)` with `hex.startsWith("#")`.
290+
help: Replace `/^#/i.test(hex)` with `hex.startsWith('#')`.
291291

292292
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
293293
╭─[prefer_string_starts_ends_with.tsx:1:5]
294294
1if (/#$/i.test(hex)) {}
295295
· ──────────
296296
╰────
297-
help: Replace `/#$/i.test(hex)` with `hex.endsWith("#")`.
297+
help: Replace `/#$/i.test(hex)` with `hex.endsWith('#')`.

0 commit comments

Comments
 (0)