3
3
#[ allow( clippy:: wildcard_imports) ]
4
4
use oxc_ast:: { ast:: * , AstKind } ;
5
5
use oxc_semantic:: Semantic ;
6
+ use oxc_syntax:: operator:: UnaryOperator ;
7
+
8
+ use crate :: rules:: eslint:: no_unused_vars:: binding_pattern:: { BindingContext , HasAnyUsedBinding } ;
6
9
7
10
use super :: binding_pattern:: CheckBinding ;
8
11
use super :: { options:: ArgsOption , NoUnusedVars , Symbol } ;
9
12
10
13
impl < ' s , ' a > Symbol < ' s , ' a > {
11
- /// Returns `true` if this function is a callback passed to another function
12
- pub fn is_function_callback ( & self ) -> bool {
14
+ /// Returns `true` if this function is a callback passed to another
15
+ /// function. Includes IIFEs.
16
+ pub fn is_callback_or_iife ( & self ) -> bool {
13
17
debug_assert ! ( self . declaration( ) . kind( ) . is_function_like( ) ) ;
14
18
15
19
for parent in self . iter_parents ( ) {
@@ -20,6 +24,10 @@ impl<'s, 'a> Symbol<'s, 'a> {
20
24
AstKind :: CallExpression ( _) => {
21
25
return true ;
22
26
}
27
+ // !function() {}; is an IIFE
28
+ AstKind :: UnaryExpression ( expr) => {
29
+ return expr. operator == UnaryOperator :: LogicalNot ;
30
+ }
23
31
_ => {
24
32
return false ;
25
33
}
@@ -56,8 +64,50 @@ impl<'s, 'a> Symbol<'s, 'a> {
56
64
57
65
false
58
66
}
67
+
68
+ fn is_declared_in_for_of_loop ( & self ) -> bool {
69
+ for parent in self . iter_parents ( ) {
70
+ match parent. kind ( ) {
71
+ AstKind :: ParenthesizedExpression ( _)
72
+ | AstKind :: VariableDeclaration ( _)
73
+ | AstKind :: BindingIdentifier ( _)
74
+ | AstKind :: SimpleAssignmentTarget ( _)
75
+ | AstKind :: AssignmentTarget ( _) => continue ,
76
+ AstKind :: ForInStatement ( ForInStatement { body, .. } )
77
+ | AstKind :: ForOfStatement ( ForOfStatement { body, .. } ) => match body {
78
+ Statement :: ReturnStatement ( _) => return true ,
79
+ Statement :: BlockStatement ( b) => {
80
+ return b
81
+ . body
82
+ . first ( )
83
+ . is_some_and ( |s| matches ! ( s, Statement :: ReturnStatement ( _) ) )
84
+ }
85
+ _ => return false ,
86
+ } ,
87
+ _ => return false ,
88
+ }
89
+ }
90
+
91
+ false
92
+ }
59
93
}
94
+
60
95
impl NoUnusedVars {
96
+ pub ( super ) fn is_allowed_class ( & self , class : & Class < ' _ > ) -> bool {
97
+ if self . ignore_class_with_static_init_block
98
+ && class. body . body . iter ( ) . any ( |el| matches ! ( el, ClassElement :: StaticBlock ( _) ) )
99
+ {
100
+ return true ;
101
+ }
102
+
103
+ false
104
+ }
105
+
106
+ #[ allow( clippy:: unused_self) ]
107
+ pub ( super ) fn is_allowed_function ( & self , symbol : & Symbol < ' _ , ' _ > ) -> bool {
108
+ symbol. is_callback_or_iife ( ) || symbol. is_function_assigned_to_same_name_variable ( )
109
+ }
110
+
61
111
/// Returns `true` if this unused variable declaration should be allowed
62
112
/// (i.e. not reported)
63
113
pub ( super ) fn is_allowed_variable_declaration < ' a > (
@@ -69,10 +119,17 @@ impl NoUnusedVars {
69
119
return true ;
70
120
}
71
121
122
+ // allow unused iterators, since they're required for valid syntax
123
+ if symbol. is_declared_in_for_of_loop ( ) {
124
+ return true ;
125
+ }
126
+
72
127
// check if res is an array/object unpacking pattern that should be ignored
73
128
matches ! ( decl. id. check_unused_binding_pattern( self , symbol) , Some ( res) if res. is_ignore( ) )
74
129
}
75
130
131
+ /// Returns `true` if this unused parameter should be allowed (i.e. not
132
+ /// reported)
76
133
pub ( super ) fn is_allowed_argument < ' a > (
77
134
& self ,
78
135
semantic : & Semantic < ' a > ,
@@ -86,17 +143,13 @@ impl NoUnusedVars {
86
143
87
144
// find FormalParameters. Should be the next parent of param, but this
88
145
// is safer.
89
- let Some ( ( params, params_id) ) = symbol
90
- . iter_parents ( )
91
- . filter_map ( |p| {
92
- if let AstKind :: FormalParameters ( params) = p. kind ( ) {
93
- Some ( ( params, p. id ( ) ) )
94
- } else {
95
- None
96
- }
97
- } )
98
- . next ( )
99
- else {
146
+ let Some ( ( params, params_id) ) = symbol. iter_parents ( ) . find_map ( |p| {
147
+ if let AstKind :: FormalParameters ( params) = p. kind ( ) {
148
+ Some ( ( params, p. id ( ) ) )
149
+ } else {
150
+ None
151
+ }
152
+ } ) else {
100
153
debug_assert ! ( false , "FormalParameter should always have a parent FormalParameters" ) ;
101
154
return false ;
102
155
} ;
@@ -116,8 +169,15 @@ impl NoUnusedVars {
116
169
}
117
170
118
171
// Parameters are always checked. Must be done after above checks,
119
- // because in those cases a parameter is required
120
- if self . args . is_none ( ) {
172
+ // because in those cases a parameter is required. However, even if
173
+ // `args` is `all`, it may be ignored using `ignoreRestSiblings` or `destructuredArrayIgnorePattern`.
174
+ if self . args . is_all ( ) {
175
+ if param. pattern . kind . is_destructuring_pattern ( ) {
176
+ // allow unpacked parameters that are ignored via destructuredArrayIgnorePattern
177
+ let maybe_unused_binding_pattern =
178
+ param. pattern . check_unused_binding_pattern ( self , symbol) ;
179
+ return maybe_unused_binding_pattern. map_or ( false , |res| res. is_ignore ( ) ) ;
180
+ }
121
181
return false ;
122
182
}
123
183
@@ -131,7 +191,7 @@ impl NoUnusedVars {
131
191
// check if this is a positional argument - unused non-positional
132
192
// arguments are never allowed
133
193
if param. pattern . kind . is_destructuring_pattern ( ) {
134
- // allow unpacked parameters that are ignored
194
+ // allow unpacked parameters that are ignored via destructuredArrayIgnorePattern
135
195
let maybe_unused_binding_pattern =
136
196
param. pattern . check_unused_binding_pattern ( self , symbol) ;
137
197
return maybe_unused_binding_pattern. map_or ( false , |res| res. is_ignore ( ) ) ;
@@ -154,15 +214,7 @@ impl NoUnusedVars {
154
214
return false ;
155
215
}
156
216
157
- params. items . iter ( ) . skip ( position + 1 ) . any ( |p| {
158
- let Some ( id) = p. pattern . get_binding_identifier ( ) else {
159
- return false ;
160
- } ;
161
- let Some ( symbol_id) = id. symbol_id . get ( ) else {
162
- return false ;
163
- } ;
164
- let symbol = Symbol :: new ( semantic, symbol_id) ;
165
- symbol. has_usages ( )
166
- } )
217
+ let ctx = BindingContext { options : self , semantic } ;
218
+ params. items . iter ( ) . skip ( position + 1 ) . any ( |p| p. pattern . has_any_used_binding ( ctx) )
167
219
}
168
220
}
0 commit comments