Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2fc083c

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

File tree

Expand file treeCollapse file tree

2 files changed

+41
-19
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+41
-19
lines changed

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

Copy file name to clipboardExpand all lines: crates/oxc_linter/src/rules/unicorn/prefer_string_starts_ends_with.rs
+27-19Lines 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

Copy file name to clipboardExpand all lines: crates/oxc_linter/src/snapshots/unicorn_prefer_string_starts_ends_with.snap
+14Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.