11use oxc_ast:: {
2- ast:: { Argument , CallExpression , Expression } ,
2+ ast:: { Argument , CallExpression , Expression , UnaryOperator } ,
33 AstKind ,
44} ;
55use oxc_diagnostics:: OxcDiagnostic ;
@@ -16,14 +16,19 @@ use crate::{
1616} ;
1717
1818fn 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
2222fn 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 ) ]
2833pub struct PreferArraySome ;
2934
@@ -55,12 +60,15 @@ declare_oxc_lint!(
5560impl 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 ! [
0 commit comments