Skip to content

Commit

Permalink
fix(linter/no-unused-vars): false positives in TS type assertions (#6397
Browse files Browse the repository at this point in the history
)

Fixes several false positive cases for referenced variables and declarations
that are inside type casts, `as` expressions, `satisfies` expressions, non-null
assertions, and the like.

```js
function foo(el) { return el + 1 }
const arr = [1, 2, 3]
const mapped = arr.map(foo as unknown as SomePredicateType)
```
  • Loading branch information
DonIsaac committed Oct 9, 2024
1 parent d3e59c6 commit ba53bc9
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl<'s, 'a> Symbol<'s, 'a> {
assert!(kind.is_function_like() || matches!(kind, AstKind::Class(_)));
}

for parent in self.iter_parents() {
for parent in self.iter_relevant_parents() {
match parent.kind() {
AstKind::MemberExpression(_) | AstKind::ParenthesizedExpression(_)
// e.g. `const x = [function foo() {}]`
Expand Down
17 changes: 15 additions & 2 deletions crates/oxc_linter/src/rules/eslint/no_unused_vars/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ impl<'s, 'a> Symbol<'s, 'a> {
self.nodes().iter_parents(self.declaration_id())
}

pub fn iter_relevant_parents(
#[inline]
pub fn iter_relevant_parents(&self) -> impl Iterator<Item = &AstNode<'a>> + Clone + '_ {
self.iter_relevant_parents_of(self.declaration_id())
}

pub fn iter_relevant_parents_of(
&self,
node_id: NodeId,
) -> impl Iterator<Item = &AstNode<'a>> + Clone + '_ {
Expand All @@ -131,7 +136,15 @@ impl<'s, 'a> Symbol<'s, 'a> {

#[inline]
const fn is_relevant_kind(kind: AstKind<'a>) -> bool {
!matches!(kind, AstKind::ParenthesizedExpression(_))
!matches!(
kind,
AstKind::ParenthesizedExpression(_)
| AstKind::TSAsExpression(_)
| AstKind::TSSatisfiesExpression(_)
| AstKind::TSInstantiationExpression(_)
| AstKind::TSNonNullExpression(_)
| AstKind::TSTypeAssertion(_)
)
}

/// <https://github.com/oxc-project/oxc/issues/4739>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@ fn test_used_declarations() {
// first put into an intermediate (e.g. an object or array)
"arr.reduce(function reducer (acc, el) { return acc + el }, 0)",
"console.log({ foo: function foo() {} })",
"console.log({ foo: function foo() {} as unknown as Function })",
"test.each([ function foo() {} ])('test some function', (fn) => { expect(fn(1)).toBe(1) })",
"export default { foo() {} }",
"const arr = [function foo() {}, function bar() {}]; console.log(arr[0]())",
Expand Down
8 changes: 4 additions & 4 deletions crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ impl<'s, 'a> Symbol<'s, 'a> {
/// type Foo = Array<Bar>
/// ```
fn is_type_self_usage(&self, reference: &Reference) -> bool {
for parent in self.iter_relevant_parents(reference.node_id()).map(AstNode::kind) {
for parent in self.iter_relevant_parents_of(reference.node_id()).map(AstNode::kind) {
match parent {
AstKind::TSTypeAliasDeclaration(decl) => {
return self == &decl.id;
Expand Down Expand Up @@ -425,7 +425,7 @@ impl<'s, 'a> Symbol<'s, 'a> {

/// Check if a [`AstNode`] is within a return statement or implicit return.
fn is_in_return_statement(&self, node_id: NodeId) -> bool {
for parent in self.iter_relevant_parents(node_id).map(AstNode::kind) {
for parent in self.iter_relevant_parents_of(node_id).map(AstNode::kind) {
match parent {
AstKind::ReturnStatement(_) => return true,
AstKind::ExpressionStatement(_) => continue,
Expand Down Expand Up @@ -652,7 +652,7 @@ impl<'s, 'a> Symbol<'s, 'a> {
/// 2. "relevant" nodes are non "transparent". For example, parenthesis are "transparent".
#[inline]
fn get_ref_relevant_node(&self, reference: &Reference) -> Option<&AstNode<'a>> {
self.iter_relevant_parents(reference.node_id()).next()
self.iter_relevant_parents_of(reference.node_id()).next()
}

/// Find the [`SymbolId`] for the nearest function declaration or expression
Expand All @@ -662,7 +662,7 @@ impl<'s, 'a> Symbol<'s, 'a> {
// name from the variable its assigned to.
let mut needs_variable_identifier = false;

for parent in self.iter_relevant_parents(node_id) {
for parent in self.iter_relevant_parents_of(node_id) {
match parent.kind() {
AstKind::Function(f) => {
return f.id.as_ref().and_then(|id| id.symbol_id.get());
Expand Down

0 comments on commit ba53bc9

Please sign in to comment.