@@ -56,159 +56,166 @@ declare_oxc_lint!(
5656
5757impl Rule for PreferArrayFind {
5858 fn run < ' a > ( & self , node : & AstNode < ' a > , ctx : & LintContext < ' a > ) {
59- // Zero index access
60- if let AstKind :: ComputedMemberExpression ( computed_member_expr) = node. kind ( )
61- && computed_member_expr. expression . is_number_0 ( )
62- && let Expression :: CallExpression ( call_expr) =
63- computed_member_expr. object . get_inner_expression ( )
64- && is_filter_call ( call_expr)
65- && !is_left_hand_side ( node, ctx)
66- {
67- ctx. diagnostic ( prefer_array_find_diagnostic ( call_expr_member_expr_property_span (
68- call_expr,
69- ) ) ) ;
70- }
71-
72- if let AstKind :: CallExpression ( call_expr) = node. kind ( )
73- && is_method_call ( call_expr, None , Some ( & [ "shift" ] ) , Some ( 0 ) , Some ( 0 ) )
74- && let Some ( Expression :: CallExpression ( filter_call_expr) ) = call_expr
75- . callee
76- . get_inner_expression ( )
77- . as_member_expression ( )
78- . map ( |expression| expression. object ( ) . get_inner_expression ( ) )
79- && is_filter_call ( filter_call_expr)
80- {
81- ctx. diagnostic ( prefer_array_find_diagnostic ( call_expr_member_expr_property_span (
82- filter_call_expr,
83- ) ) ) ;
84- }
59+ match node. kind ( ) {
60+ AstKind :: ComputedMemberExpression ( computed_member_expr) => {
61+ // Zero index access
62+ if computed_member_expr. expression . is_number_0 ( )
63+ && let Expression :: CallExpression ( call_expr) =
64+ computed_member_expr. object . get_inner_expression ( )
65+ && is_filter_call ( call_expr)
66+ && !is_left_hand_side ( node, ctx)
67+ {
68+ ctx. diagnostic ( prefer_array_find_diagnostic (
69+ call_expr_member_expr_property_span ( call_expr) ,
70+ ) ) ;
71+ }
72+ }
73+ AstKind :: CallExpression ( call_expr) => {
74+ if is_method_call ( call_expr, None , Some ( & [ "shift" ] ) , Some ( 0 ) , Some ( 0 ) )
75+ && let Some ( Expression :: CallExpression ( filter_call_expr) ) = call_expr
76+ . callee
77+ . get_inner_expression ( )
78+ . as_member_expression ( )
79+ . map ( |expression| expression. object ( ) . get_inner_expression ( ) )
80+ && is_filter_call ( filter_call_expr)
81+ {
82+ ctx. diagnostic ( prefer_array_find_diagnostic (
83+ call_expr_member_expr_property_span ( filter_call_expr) ,
84+ ) ) ;
85+ }
8586
86- // `const [foo] = array.filter()`
87- if let AstKind :: VariableDeclarator ( var_decl) = node. kind ( )
88- && let BindingPatternKind :: ArrayPattern ( array_pat) = & var_decl. id . kind
89- && array_pat. elements . len ( ) == 1
90- && array_pat. elements [ 0 ] . is_some ( )
91- && let Some ( Expression :: CallExpression ( array_filter) ) = & var_decl. init
92- && is_filter_call ( array_filter)
93- {
94- ctx. diagnostic ( prefer_array_find_diagnostic ( call_expr_member_expr_property_span (
95- array_filter,
96- ) ) ) ;
97- }
87+ // `array.filter().at(0)`
88+ // `array.filter().at(-1)`
89+ if is_method_call ( call_expr, None , Some ( & [ "at" ] ) , Some ( 1 ) , Some ( 1 ) )
90+ && call_expr. arguments . first ( ) . is_some_and ( |arg| {
91+ arg. as_expression ( ) . is_some_and ( |x| match x {
92+ Expression :: NumericLiteral ( _) if x. is_number_value ( 0.0 ) => true ,
93+ Expression :: UnaryExpression ( u)
94+ if u. operator == UnaryOperator :: UnaryNegation =>
95+ {
96+ u. argument . is_number_value ( 1.0 )
97+ }
98+ _ => false ,
99+ } )
100+ } )
101+ && let Some ( Expression :: CallExpression ( filter_call_expr) ) = call_expr
102+ . callee
103+ . get_inner_expression ( )
104+ . as_member_expression ( )
105+ . map ( |expression| expression. object ( ) . get_inner_expression ( ) )
106+ && is_filter_call ( filter_call_expr)
107+ {
108+ ctx. diagnostic ( prefer_array_find_diagnostic (
109+ call_expr_member_expr_property_span ( filter_call_expr) ,
110+ ) ) ;
111+ }
98112
99- // `[foo] = array.filter()`
100- if let AstKind :: AssignmentExpression ( assignment_expr) = node. kind ( )
101- && let AssignmentTarget :: ArrayAssignmentTarget ( array_assignment_target) =
102- & assignment_expr. left
103- && array_assignment_target. elements . len ( ) == 1
104- && array_assignment_target. elements [ 0 ] . is_some ( )
105- && let Expression :: CallExpression ( array_filter) = & assignment_expr. right
106- && is_filter_call ( array_filter)
107- {
108- ctx. diagnostic ( prefer_array_find_diagnostic ( call_expr_member_expr_property_span (
109- array_filter,
110- ) ) ) ;
111- }
113+ // `array.filter().pop()`
114+ if is_method_call ( call_expr, None , Some ( & [ "pop" ] ) , Some ( 0 ) , Some ( 0 ) )
115+ && let Some ( Expression :: CallExpression ( filter_call_expr) ) = call_expr
116+ . callee
117+ . get_inner_expression ( )
118+ . as_member_expression ( )
119+ . map ( |expression| expression. object ( ) . get_inner_expression ( ) )
120+ && is_filter_call ( filter_call_expr)
121+ {
122+ ctx. diagnostic ( prefer_array_find_diagnostic (
123+ call_expr_member_expr_property_span ( filter_call_expr) ,
124+ ) ) ;
125+ }
126+ }
127+ AstKind :: VariableDeclarator ( var_decl) => {
128+ // `const [foo] = array.filter()`
129+ if let BindingPatternKind :: ArrayPattern ( array_pat) = & var_decl. id . kind
130+ && array_pat. elements . len ( ) == 1
131+ && array_pat. elements [ 0 ] . is_some ( )
132+ && let Some ( Expression :: CallExpression ( array_filter) ) = & var_decl. init
133+ && is_filter_call ( array_filter)
134+ {
135+ ctx. diagnostic ( prefer_array_find_diagnostic (
136+ call_expr_member_expr_property_span ( array_filter) ,
137+ ) ) ;
138+ }
112139
113- // `const foo = array.filter(); foo[0]; [bar] = foo`
114- if let AstKind :: VariableDeclarator ( var_decl) = node. kind ( )
115- && let Some ( Expression :: CallExpression ( call_expr) ) = & var_decl. init
116- && is_filter_call ( call_expr)
117- && !matches ! (
118- ctx. nodes( ) . ancestor_kinds( node. id( ) ) . nth( 1 ) ,
119- Some ( AstKind :: ExportDefaultDeclaration ( _) | AstKind :: ExportNamedDeclaration ( _) )
120- )
121- && let Some ( ident) = var_decl. id . kind . get_binding_identifier ( )
122- {
123- let mut zero_index_nodes = Vec :: new ( ) ;
124- let mut destructuring_nodes = Vec :: new ( ) ;
140+ // `const foo = array.filter(); foo[0]; [bar] = foo`
141+ if let Some ( Expression :: CallExpression ( call_expr) ) = & var_decl. init
142+ && is_filter_call ( call_expr)
143+ && !matches ! (
144+ ctx. nodes( ) . ancestor_kinds( node. id( ) ) . nth( 1 ) ,
145+ Some (
146+ AstKind :: ExportDefaultDeclaration ( _)
147+ | AstKind :: ExportNamedDeclaration ( _)
148+ )
149+ )
150+ && let Some ( ident) = var_decl. id . kind . get_binding_identifier ( )
151+ {
152+ let mut zero_index_nodes = Vec :: new ( ) ;
153+ let mut destructuring_nodes = Vec :: new ( ) ;
125154
126- let mut is_used_elsewhere = false ;
155+ let mut is_used_elsewhere = false ;
127156
128- for reference in ctx. symbol_references ( ident. symbol_id ( ) ) {
129- match ctx. nodes ( ) . parent_kind ( reference. node_id ( ) ) {
130- AstKind :: ComputedMemberExpression ( c) if c. expression . is_number_0 ( ) => {
131- zero_index_nodes. push ( reference) ;
132- }
133- AstKind :: VariableDeclarator ( var_declarator) => {
134- if let BindingPatternKind :: ArrayPattern ( array_pat) = & var_declarator. id . kind
135- && array_pat. elements . len ( ) == 1
136- && array_pat. elements [ 0 ] . is_some ( )
137- {
138- destructuring_nodes. push ( reference) ;
139- }
140- }
141- AstKind :: AssignmentExpression ( assignment_expr) => {
142- // Check for array destructuring: [foo] = items
143- if let AssignmentTarget :: ArrayAssignmentTarget ( target) =
144- & assignment_expr. left
145- {
146- if target. elements . len ( ) == 1 && target. elements [ 0 ] . is_some ( ) {
147- destructuring_nodes. push ( reference) ;
157+ for reference in ctx. symbol_references ( ident. symbol_id ( ) ) {
158+ match ctx. nodes ( ) . parent_kind ( reference. node_id ( ) ) {
159+ AstKind :: ComputedMemberExpression ( c) if c. expression . is_number_0 ( ) => {
160+ zero_index_nodes. push ( reference) ;
148161 }
149- } else if let Some ( SimpleAssignmentTarget :: AssignmentTargetIdentifier (
150- ident,
151- ) ) = assignment_expr. left . as_simple_assignment_target ( )
152- {
153- // Check for simple reassignment: items = something
154- if ident. span == ctx. nodes ( ) . get_node ( reference. node_id ( ) ) . span ( ) {
155- is_used_elsewhere = true ; // Variable is being reassigned
162+ AstKind :: VariableDeclarator ( var_declarator) => {
163+ if let BindingPatternKind :: ArrayPattern ( array_pat) =
164+ & var_declarator. id . kind
165+ && array_pat. elements . len ( ) == 1
166+ && array_pat. elements [ 0 ] . is_some ( )
167+ {
168+ destructuring_nodes. push ( reference) ;
169+ }
156170 }
171+ AstKind :: AssignmentExpression ( assignment_expr) => {
172+ // Check for array destructuring: [foo] = items
173+ if let AssignmentTarget :: ArrayAssignmentTarget ( target) =
174+ & assignment_expr. left
175+ {
176+ if target. elements . len ( ) == 1 && target. elements [ 0 ] . is_some ( ) {
177+ destructuring_nodes. push ( reference) ;
178+ }
179+ } else if let Some (
180+ SimpleAssignmentTarget :: AssignmentTargetIdentifier ( ident) ,
181+ ) = assignment_expr. left . as_simple_assignment_target ( )
182+ {
183+ // Check for simple reassignment: items = something
184+ if ident. span
185+ == ctx. nodes ( ) . get_node ( reference. node_id ( ) ) . span ( )
186+ {
187+ is_used_elsewhere = true ; // Variable is being reassigned
188+ }
189+ }
190+ }
191+ _ => is_used_elsewhere = true ,
157192 }
158193 }
159- _ => is_used_elsewhere = true ,
160- }
161- }
162-
163- if !is_used_elsewhere
164- && ( !zero_index_nodes. is_empty ( ) || !destructuring_nodes. is_empty ( ) )
165- {
166- ctx. diagnostic ( prefer_array_find_diagnostic ( call_expr_member_expr_property_span (
167- call_expr,
168- ) ) ) ;
169- }
170- }
171194
172- // `array.filter().at(0)`
173- // `array.filter().at(-1)`
174- if let AstKind :: CallExpression ( at_call_expr) = node. kind ( )
175- && is_method_call ( at_call_expr, None , Some ( & [ "at" ] ) , Some ( 1 ) , Some ( 1 ) )
176- && at_call_expr. arguments . first ( ) . is_some_and ( |arg| {
177- arg. as_expression ( ) . is_some_and ( |x| match x {
178- Expression :: NumericLiteral ( _) if x. is_number_value ( 0.0 ) => true ,
179- Expression :: UnaryExpression ( u)
180- if u. operator == UnaryOperator :: UnaryNegation =>
195+ if !is_used_elsewhere
196+ && ( !zero_index_nodes. is_empty ( ) || !destructuring_nodes. is_empty ( ) )
181197 {
182- u. argument . is_number_value ( 1.0 )
198+ ctx. diagnostic ( prefer_array_find_diagnostic (
199+ call_expr_member_expr_property_span ( call_expr) ,
200+ ) ) ;
183201 }
184- _ => false ,
185- } )
186- } )
187- && let Some ( Expression :: CallExpression ( filter_call_expr) ) = at_call_expr
188- . callee
189- . get_inner_expression ( )
190- . as_member_expression ( )
191- . map ( |expression| expression. object ( ) . get_inner_expression ( ) )
192- && is_filter_call ( filter_call_expr)
193- {
194- ctx. diagnostic ( prefer_array_find_diagnostic ( call_expr_member_expr_property_span (
195- filter_call_expr,
196- ) ) ) ;
197- }
198-
199- // `array.filter().pop()`
200- if let AstKind :: CallExpression ( pop_call_expr) = node. kind ( )
201- && is_method_call ( pop_call_expr, None , Some ( & [ "pop" ] ) , Some ( 0 ) , Some ( 0 ) )
202- && let Some ( Expression :: CallExpression ( filter_call_expr) ) = pop_call_expr
203- . callee
204- . get_inner_expression ( )
205- . as_member_expression ( )
206- . map ( |expression| expression. object ( ) . get_inner_expression ( ) )
207- && is_filter_call ( filter_call_expr)
208- {
209- ctx. diagnostic ( prefer_array_find_diagnostic ( call_expr_member_expr_property_span (
210- filter_call_expr,
211- ) ) ) ;
202+ }
203+ }
204+ AstKind :: AssignmentExpression ( assignment_expr) => {
205+ // `[foo] = array.filter()`
206+ if let AssignmentTarget :: ArrayAssignmentTarget ( array_assignment_target) =
207+ & assignment_expr. left
208+ && array_assignment_target. elements . len ( ) == 1
209+ && array_assignment_target. elements [ 0 ] . is_some ( )
210+ && let Expression :: CallExpression ( array_filter) = & assignment_expr. right
211+ && is_filter_call ( array_filter)
212+ {
213+ ctx. diagnostic ( prefer_array_find_diagnostic (
214+ call_expr_member_expr_property_span ( array_filter) ,
215+ ) ) ;
216+ }
217+ }
218+ _ => { }
212219 }
213220 }
214221}
0 commit comments