Skip to content

Commit 2fc083c

Browse files
committed
fix(linter): incorrect fix for prefer start ends with (#10525)
fixes #10523
1 parent 6e40fac commit 2fc083c

File tree

2 files changed

+41
-19
lines changed

2 files changed

+41
-19
lines changed

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

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,14 @@ impl Rule for PreferStringStartsEndsWith {
8787
};
8888

8989
match err_kind {
90-
ErrorKind::StartsWith => {
90+
ErrorKind::StartsWith(_) => {
9191
ctx.diagnostic_with_fix(starts_with(member_expr.span()), |fixer| {
92-
do_fix(fixer, err_kind, call_expr, pattern_text)
92+
do_fix(fixer, err_kind, call_expr)
9393
});
9494
}
95-
ErrorKind::EndsWith => {
95+
ErrorKind::EndsWith(_) => {
9696
ctx.diagnostic_with_fix(ends_with(member_expr.span()), |fixer| {
97-
do_fix(fixer, err_kind, call_expr, pattern_text)
97+
do_fix(fixer, err_kind, call_expr)
9898
});
9999
}
100100
}
@@ -105,13 +105,17 @@ fn do_fix<'a>(
105105
fixer: RuleFixer<'_, 'a>,
106106
err_kind: ErrorKind,
107107
call_expr: &CallExpression<'a>,
108-
pattern_text: &str,
109108
) -> RuleFix<'a> {
110109
let Some(target_span) = can_replace(call_expr) else { return fixer.noop() };
111110
let (argument, method) = match err_kind {
112-
ErrorKind::StartsWith => (pattern_text.trim_start_matches('^'), "startsWith"),
113-
ErrorKind::EndsWith => (pattern_text.trim_end_matches('$'), "endsWith"),
111+
ErrorKind::StartsWith(arg) => {
112+
(arg.into_iter().map(std::char::from_u32).collect::<Option<String>>(), "startsWith")
113+
}
114+
ErrorKind::EndsWith(arg) => {
115+
(arg.into_iter().map(std::char::from_u32).collect::<Option<String>>(), "endsWith")
116+
}
114117
};
118+
let Some(argument) = argument else { return fixer.noop() };
115119
let fix_text = format!(r#"{}.{}("{}")"#, fixer.source_range(target_span), method, argument);
116120

117121
fixer.replace(call_expr.span, fix_text)
@@ -134,10 +138,9 @@ fn can_replace(call_expr: &CallExpression) -> Option<Span> {
134138
}
135139
}
136140

137-
#[derive(Clone, Copy)]
138141
enum ErrorKind {
139-
StartsWith,
140-
EndsWith,
142+
StartsWith(Vec<u32>),
143+
EndsWith(Vec<u32>),
141144
}
142145

143146
fn check_regex(regexp_lit: &RegExpLiteral, pattern_text: &str) -> Option<ErrorKind> {
@@ -156,21 +159,24 @@ fn check_regex(regexp_lit: &RegExpLiteral, pattern_text: &str) -> Option<ErrorKi
156159
let pattern_terms = alternatives.first().map(|it| &it.body)?;
157160

158161
if let Some(Term::BoundaryAssertion(boundary_assert)) = pattern_terms.first() {
159-
if boundary_assert.kind == BoundaryAssertionKind::Start
160-
&& pattern_terms.iter().skip(1).all(|term| matches!(term, Term::Character(_)))
161-
{
162-
return Some(ErrorKind::StartsWith);
162+
if boundary_assert.kind == BoundaryAssertionKind::Start {
163+
return pattern_terms
164+
.iter()
165+
.skip(1)
166+
.map(|t| if let Term::Character(c) = t { Some(c.value) } else { None })
167+
.collect::<Option<Vec<_>>>()
168+
.map(ErrorKind::StartsWith);
163169
}
164170
}
165171

166172
if let Some(Term::BoundaryAssertion(boundary_assert)) = pattern_terms.last() {
167-
if boundary_assert.kind == BoundaryAssertionKind::End
168-
&& pattern_terms
173+
if boundary_assert.kind == BoundaryAssertionKind::End {
174+
return pattern_terms
169175
.iter()
170176
.take(pattern_terms.len() - 1)
171-
.all(|term| matches!(term, Term::Character(_)))
172-
{
173-
return Some(ErrorKind::EndsWith);
177+
.map(|t| if let Term::Character(c) = t { Some(c.value) } else { None })
178+
.collect::<Option<Vec<_>>>()
179+
.map(ErrorKind::EndsWith);
174180
}
175181
}
176182

@@ -227,6 +233,8 @@ fn test() {
227233
let fail = vec![
228234
r"/^foo/.test(bar)",
229235
r"/foo$/.test(bar)",
236+
r"/\$$/.test(bar)",
237+
r"/^\^/.test(bar)",
230238
r"/^!/.test(bar)",
231239
r"/!$/.test(bar)",
232240
r"/^ /.test(bar)",

crates/oxc_linter/src/snapshots/unicorn_prefer_string_starts_ends_with.snap

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ source: crates/oxc_linter/src/tester.rs
1515
╰────
1616
help: Replace `/foo$/.test(bar)` with `bar.endsWith("foo")`.
1717

18+
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#endsWith over a regex with a dollar sign.
19+
╭─[prefer_string_starts_ends_with.tsx:1:1]
20+
1/\$$/.test(bar)
21+
· ──────────
22+
╰────
23+
help: Replace `/\$$/.test(bar)` with `bar.endsWith("$")`.
24+
25+
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
26+
╭─[prefer_string_starts_ends_with.tsx:1:1]
27+
1/^\^/.test(bar)
28+
· ──────────
29+
╰────
30+
help: Replace `/^\^/.test(bar)` with `bar.startsWith("^")`.
31+
1832
eslint-plugin-unicorn(prefer-string-starts-ends-with): Prefer String#startsWith over a regex with a caret.
1933
╭─[prefer_string_starts_ends_with.tsx:1:1]
2034
1/^!/.test(bar)

0 commit comments

Comments
 (0)