Skip to content

Commit 311fcf4

Browse files
committed
cleanup
1 parent daa2d76 commit 311fcf4

File tree

11 files changed

+2176
-330
lines changed

11 files changed

+2176
-330
lines changed

.typos.toml

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ extend-exclude = [
77
"**/*.snap",
88
"**/*/CHANGELOG.md",
99
"crates/oxc_linter/fixtures",
10+
"crates/oxc_linter/src/rules/eslint/no_unused_vars/options.rs",
11+
"crates/oxc_linter/src/rules/eslint/no_unused_vars/tests/eslint.rs",
1012
"crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs",
1113
"crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs",
1214
"crates/oxc_linter/src/rules/react/no_unknown_property.rs",

crates/oxc_linter/src/rules/eslint/no_unused_vars/allowed.rs

+78-26
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
#[allow(clippy::wildcard_imports)]
44
use oxc_ast::{ast::*, AstKind};
55
use oxc_semantic::Semantic;
6+
use oxc_syntax::operator::UnaryOperator;
7+
8+
use crate::rules::eslint::no_unused_vars::binding_pattern::{BindingContext, HasAnyUsedBinding};
69

710
use super::binding_pattern::CheckBinding;
811
use super::{options::ArgsOption, NoUnusedVars, Symbol};
912

1013
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 {
1317
debug_assert!(self.declaration().kind().is_function_like());
1418

1519
for parent in self.iter_parents() {
@@ -20,6 +24,10 @@ impl<'s, 'a> Symbol<'s, 'a> {
2024
AstKind::CallExpression(_) => {
2125
return true;
2226
}
27+
// !function() {}; is an IIFE
28+
AstKind::UnaryExpression(expr) => {
29+
return expr.operator == UnaryOperator::LogicalNot;
30+
}
2331
_ => {
2432
return false;
2533
}
@@ -56,8 +64,50 @@ impl<'s, 'a> Symbol<'s, 'a> {
5664

5765
false
5866
}
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+
}
5993
}
94+
6095
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+
61111
/// Returns `true` if this unused variable declaration should be allowed
62112
/// (i.e. not reported)
63113
pub(super) fn is_allowed_variable_declaration<'a>(
@@ -69,10 +119,17 @@ impl NoUnusedVars {
69119
return true;
70120
}
71121

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+
72127
// check if res is an array/object unpacking pattern that should be ignored
73128
matches!(decl.id.check_unused_binding_pattern(self, symbol), Some(res) if res.is_ignore())
74129
}
75130

131+
/// Returns `true` if this unused parameter should be allowed (i.e. not
132+
/// reported)
76133
pub(super) fn is_allowed_argument<'a>(
77134
&self,
78135
semantic: &Semantic<'a>,
@@ -86,17 +143,13 @@ impl NoUnusedVars {
86143

87144
// find FormalParameters. Should be the next parent of param, but this
88145
// 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 {
100153
debug_assert!(false, "FormalParameter should always have a parent FormalParameters");
101154
return false;
102155
};
@@ -116,8 +169,15 @@ impl NoUnusedVars {
116169
}
117170

118171
// 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+
}
121181
return false;
122182
}
123183

@@ -131,7 +191,7 @@ impl NoUnusedVars {
131191
// check if this is a positional argument - unused non-positional
132192
// arguments are never allowed
133193
if param.pattern.kind.is_destructuring_pattern() {
134-
// allow unpacked parameters that are ignored
194+
// allow unpacked parameters that are ignored via destructuredArrayIgnorePattern
135195
let maybe_unused_binding_pattern =
136196
param.pattern.check_unused_binding_pattern(self, symbol);
137197
return maybe_unused_binding_pattern.map_or(false, |res| res.is_ignore());
@@ -154,15 +214,7 @@ impl NoUnusedVars {
154214
return false;
155215
}
156216

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))
167219
}
168220
}

0 commit comments

Comments
 (0)