Skip to content

Commit 9002e97

Browse files
authored
fix(linter): add proper support for findIndex and findLastIndex for unicorn/prefer-array-some (#7405)
closes #7404
1 parent f2cfed1 commit 9002e97

2 files changed

Lines changed: 251 additions & 14 deletions

File tree

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

Lines changed: 132 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use oxc_ast::{
2-
ast::{Argument, CallExpression, Expression},
2+
ast::{Argument, CallExpression, Expression, UnaryOperator},
33
AstKind,
44
};
55
use oxc_diagnostics::OxcDiagnostic;
@@ -16,14 +16,19 @@ use crate::{
1616
};
1717

1818
fn over_method(span: Span) -> OxcDiagnostic {
19-
OxcDiagnostic::warn("Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.").with_label(span)
19+
OxcDiagnostic::warn("Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.").with_label(span)
2020
}
2121

2222
fn non_zero_filter(span: Span) -> OxcDiagnostic {
2323
OxcDiagnostic::warn("Prefer `.some(…)` over non-zero length check from `.filter(…)`.")
2424
.with_label(span)
2525
}
2626

27+
fn negative_one_or_zero_filter(span: Span) -> OxcDiagnostic {
28+
OxcDiagnostic::warn("Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.")
29+
.with_label(span)
30+
}
31+
2732
#[derive(Debug, Default, Clone)]
2833
pub struct PreferArraySome;
2934

@@ -55,12 +60,15 @@ declare_oxc_lint!(
5560
impl Rule for PreferArraySome {
5661
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
5762
match node.kind() {
63+
// `.find(…)`
64+
// `.findLast(…)`
5865
AstKind::CallExpression(call_expr) => {
5966
if !is_method_call(call_expr, None, Some(&["find", "findLast"]), Some(1), Some(2)) {
6067
return;
6168
}
6269

6370
let is_compare = is_checking_undefined(node, call_expr, ctx);
71+
6472
if !is_compare && !is_boolean_node(node, ctx) {
6573
return;
6674
}
@@ -87,6 +95,97 @@ impl Rule for PreferArraySome {
8795
);
8896
}
8997
AstKind::BinaryExpression(bin_expr) => {
98+
// `.{findIndex,findLastIndex}(…) !== -1`
99+
// `.{findIndex,findLastIndex}(…) != -1`
100+
// `.{findIndex,findLastIndex}(…) > -1`
101+
// `.{findIndex,findLastIndex}(…) === -1`
102+
// `.{findIndex,findLastIndex}(…) == -1`
103+
// `.{findIndex,findLastIndex}(…) >= 0`
104+
// `.{findIndex,findLastIndex}(…) < 0`
105+
let with_negative_one = matches!(
106+
bin_expr.operator,
107+
BinaryOperator::StrictInequality
108+
| BinaryOperator::Inequality
109+
| BinaryOperator::GreaterThan
110+
| BinaryOperator::StrictEquality
111+
| BinaryOperator::Equality
112+
) && matches!(
113+
bin_expr.right.without_parentheses(),
114+
Expression::UnaryExpression(_)
115+
);
116+
117+
let matches_against_zero = matches!(
118+
bin_expr.operator,
119+
BinaryOperator::GreaterEqualThan | BinaryOperator::LessThan
120+
);
121+
122+
if with_negative_one {
123+
if let Expression::UnaryExpression(right_unary_expr) =
124+
&bin_expr.right.without_parentheses()
125+
{
126+
if matches!(right_unary_expr.operator, UnaryOperator::UnaryNegation)
127+
&& right_unary_expr.argument.is_number_literal()
128+
&& right_unary_expr.argument.is_number_value(1_f64)
129+
{
130+
let Expression::CallExpression(left_call_expr) =
131+
&bin_expr.left.without_parentheses()
132+
else {
133+
return;
134+
};
135+
136+
let Some(argument) = left_call_expr.arguments.first() else {
137+
return;
138+
};
139+
140+
if matches!(argument, Argument::SpreadElement(_)) {
141+
return;
142+
}
143+
144+
if is_method_call(
145+
left_call_expr,
146+
None,
147+
Some(&["findIndex", "findLastIndex"]),
148+
None,
149+
Some(1),
150+
) {
151+
// TODO: fixer
152+
ctx.diagnostic(negative_one_or_zero_filter(
153+
call_expr_method_callee_info(left_call_expr).unwrap().0,
154+
));
155+
}
156+
}
157+
}
158+
}
159+
160+
if matches_against_zero {
161+
let Expression::NumericLiteral(right_num_lit) = &bin_expr.right else {
162+
return;
163+
};
164+
165+
let Expression::CallExpression(left_call_expr) =
166+
&bin_expr.left.without_parentheses()
167+
else {
168+
return;
169+
};
170+
171+
if right_num_lit.raw == "0"
172+
&& is_method_call(
173+
left_call_expr,
174+
None,
175+
Some(&["findIndex", "findLastIndex"]),
176+
None,
177+
Some(1),
178+
)
179+
{
180+
// TODO: fixer
181+
ctx.diagnostic(negative_one_or_zero_filter(
182+
call_expr_method_callee_info(left_call_expr).unwrap().0,
183+
));
184+
}
185+
}
186+
187+
// `.filter(…).length > 0`
188+
// `.filter(…).length !== 0`
90189
if !matches!(
91190
bin_expr.operator,
92191
BinaryOperator::GreaterThan | BinaryOperator::StrictInequality
@@ -281,6 +380,17 @@ fn test() {
281380
r"foo.find(fn) >= undefined",
282381
r"foo.find(fn) instanceof undefined",
283382
r#"typeof foo.find(fn) === "undefined""#,
383+
// findIndex: negative one
384+
r"foo.notMatchedMethod(bar) !== -1",
385+
r"new foo.findIndex(bar) !== -1",
386+
r"foo.findIndex(bar, extraArgument) !== -1",
387+
r"foo.findIndex(bar) instanceof -1",
388+
r"foo.findIndex(...bar) !== -1",
389+
// findLastIndex: negative one
390+
r"new foo.findLastIndex(bar) !== -1",
391+
r"foo.findLastIndex(bar, extraArgument) !== -1",
392+
r"foo.findLastIndex(bar) instanceof -1",
393+
r"foo.findLastIndex(...bar) !== -1",
284394
];
285395

286396
let fail = vec![
@@ -297,6 +407,26 @@ fn test() {
297407
r"foo.find(fn) != undefined",
298408
r"foo.find(fn) !== undefined",
299409
r#"a = (( ((foo.find(fn))) == ((null)) )) ? "no" : "yes";"#,
410+
// findIndex: negative one || ( >= || < ) 0
411+
r"foo.findIndex(bar) !== -1",
412+
r"foo.findIndex(bar) != -1",
413+
r"foo.findIndex(bar) > - 1",
414+
r"foo.findIndex(bar) === -1",
415+
r"foo.findIndex(bar) == - 1",
416+
r"foo.findIndex(bar) >= 0",
417+
r"foo.findIndex(bar) < 0",
418+
r"foo.findIndex(bar) !== (( - 1 ))",
419+
r"foo.findIndex(element => element.bar === 1) !== (( - 1 ))",
420+
// findLastIndex: negative one || ( >= || < ) 0
421+
r"foo.findLastIndex(bar) !== -1",
422+
r"foo.findLastIndex(bar) != -1",
423+
r"foo.findLastIndex(bar) > - 1",
424+
r"foo.findLastIndex(bar) === -1",
425+
r"foo.findLastIndex(bar) == - 1",
426+
r"foo.findLastIndex(bar) >= 0",
427+
r"foo.findLastIndex(bar) < 0",
428+
r"foo.findLastIndex(bar) !== (( - 1 ))",
429+
r"foo.findLastIndex(element => element.bar === 1) !== (( - 1 ))",
300430
];
301431

302432
let fix = vec![

crates/oxc_linter/src/snapshots/prefer_array_some.snap

Lines changed: 119 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
11
---
22
source: crates/oxc_linter/src/tester.rs
3-
snapshot_kind: text
43
---
5-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
4+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
65
╭─[prefer_array_some.tsx:1:9]
76
1if (foo.find(fn)) {}
87
· ────
98
╰────
109
help: Replace `find` with `some`.
1110

12-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
11+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
1312
╭─[prefer_array_some.tsx:1:9]
1413
1if (foo.findLast(fn)) {}
1514
· ────────
1615
╰────
1716
help: Replace `findLast` with `some`.
1817

19-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
18+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
2019
╭─[prefer_array_some.tsx:1:11]
2120
1if (array.find(element => element === "🦄")) {}
2221
· ────
2322
╰────
2423
help: Replace `find` with `some`.
2524

26-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
25+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
2726
╭─[prefer_array_some.tsx:1:19]
2827
1const foo = array.find(element => element === "🦄") ? bar : baz;
2928
· ────
@@ -44,51 +43,159 @@ snapshot_kind: text
4443
╰────
4544
help: Replace `filter` with `some`.
4645

47-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
46+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
4847
╭─[prefer_array_some.tsx:1:5]
4948
1foo.find(fn) == null
5049
· ────
5150
╰────
5251
help: Replace `find` with `some`.
5352

54-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
53+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
5554
╭─[prefer_array_some.tsx:1:5]
5655
1foo.find(fn) == undefined
5756
· ────
5857
╰────
5958
help: Replace `find` with `some`.
6059

61-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
60+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
6261
╭─[prefer_array_some.tsx:1:5]
6362
1foo.find(fn) === undefined
6463
· ────
6564
╰────
6665
help: Replace `find` with `some`.
6766

68-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
67+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
6968
╭─[prefer_array_some.tsx:1:5]
7069
1foo.find(fn) != null
7170
· ────
7271
╰────
7372
help: Replace `find` with `some`.
7473

75-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
74+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
7675
╭─[prefer_array_some.tsx:1:5]
7776
1foo.find(fn) != undefined
7877
· ────
7978
╰────
8079
help: Replace `find` with `some`.
8180

82-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
81+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
8382
╭─[prefer_array_some.tsx:1:5]
8483
1foo.find(fn) !== undefined
8584
· ────
8685
╰────
8786
help: Replace `find` with `some`.
8887

89-
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)`or `.findLast(…)`.
88+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.find(…)` or `.findLast(…)`.
9089
╭─[prefer_array_some.tsx:1:14]
9190
1a = (( ((foo.find(fn))) == ((null)) )) ? "no" : "yes";
9291
· ────
9392
╰────
9493
help: Replace `find` with `some`.
94+
95+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
96+
╭─[prefer_array_some.tsx:1:5]
97+
1foo.findIndex(bar) !== -1
98+
· ─────────
99+
╰────
100+
101+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
102+
╭─[prefer_array_some.tsx:1:5]
103+
1foo.findIndex(bar) != -1
104+
· ─────────
105+
╰────
106+
107+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
108+
╭─[prefer_array_some.tsx:1:5]
109+
1foo.findIndex(bar) > - 1
110+
· ─────────
111+
╰────
112+
113+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
114+
╭─[prefer_array_some.tsx:1:5]
115+
1foo.findIndex(bar) === -1
116+
· ─────────
117+
╰────
118+
119+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
120+
╭─[prefer_array_some.tsx:1:5]
121+
1foo.findIndex(bar) == - 1
122+
· ─────────
123+
╰────
124+
125+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
126+
╭─[prefer_array_some.tsx:1:5]
127+
1foo.findIndex(bar) >= 0
128+
· ─────────
129+
╰────
130+
131+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
132+
╭─[prefer_array_some.tsx:1:5]
133+
1foo.findIndex(bar) < 0
134+
· ─────────
135+
╰────
136+
137+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
138+
╭─[prefer_array_some.tsx:1:5]
139+
1foo.findIndex(bar) !== (( - 1 ))
140+
· ─────────
141+
╰────
142+
143+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
144+
╭─[prefer_array_some.tsx:1:5]
145+
1foo.findIndex(element => element.bar === 1) !== (( - 1 ))
146+
· ─────────
147+
╰────
148+
149+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
150+
╭─[prefer_array_some.tsx:1:5]
151+
1foo.findLastIndex(bar) !== -1
152+
· ─────────────
153+
╰────
154+
155+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
156+
╭─[prefer_array_some.tsx:1:5]
157+
1foo.findLastIndex(bar) != -1
158+
· ─────────────
159+
╰────
160+
161+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
162+
╭─[prefer_array_some.tsx:1:5]
163+
1foo.findLastIndex(bar) > - 1
164+
· ─────────────
165+
╰────
166+
167+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
168+
╭─[prefer_array_some.tsx:1:5]
169+
1foo.findLastIndex(bar) === -1
170+
· ─────────────
171+
╰────
172+
173+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
174+
╭─[prefer_array_some.tsx:1:5]
175+
1foo.findLastIndex(bar) == - 1
176+
· ─────────────
177+
╰────
178+
179+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
180+
╭─[prefer_array_some.tsx:1:5]
181+
1foo.findLastIndex(bar) >= 0
182+
· ─────────────
183+
╰────
184+
185+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
186+
╭─[prefer_array_some.tsx:1:5]
187+
1foo.findLastIndex(bar) < 0
188+
· ─────────────
189+
╰────
190+
191+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
192+
╭─[prefer_array_some.tsx:1:5]
193+
1foo.findLastIndex(bar) !== (( - 1 ))
194+
· ─────────────
195+
╰────
196+
197+
eslint-plugin-unicorn(prefer-array-some): Prefer `.some(…)` over `.findIndex(…)` or `.findLastIndex(…)`.
198+
╭─[prefer_array_some.tsx:1:5]
199+
1foo.findLastIndex(element => element.bar === 1) !== (( - 1 ))
200+
· ─────────────
201+
╰────

0 commit comments

Comments
 (0)