diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index e8709c561aac9..29162b4103da0 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -279,6 +279,11 @@ impl<'a> Expression<'a> { false } } + + /// Returns `true` if this is an [assignment expression](AssignmentExpression). + pub fn is_assignment(&self) -> bool { + matches!(self, Expression::AssignmentExpression(_)) + } } impl<'a> fmt::Display for IdentifierName<'a> { diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/tests/oxc.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/tests/oxc.rs index ba16292523178..5c21298d8a680 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/tests/oxc.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/tests/oxc.rs @@ -208,6 +208,30 @@ fn test_vars_discarded_reads() { new Test(); ", + "function foo(a) { + const Bar = require('./bar'); + a instanceof Bar && (this.a = a); + } + foo(1) + ", + "function foo(a) { + const Bar = require('./bar'); + (a instanceof Bar && (this.a = a), this.b = 1); + } + foo(1) + ", + "function foo(a) { + const Bar = require('./bar'); + (a instanceof Bar && (this.a ||= 2), this.b = 1); + } + foo(1) + ", + "function foo(a) { + const bar = require('./bar'); + (a in bar && (this.a = a), this.b = 1); + } + foo(1) + ", ]; let fail = vec![ diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs index 46dc3bca48fdc..b888442265586 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs @@ -559,6 +559,23 @@ impl<'s, 'a> Symbol<'s, 'a> { return false; } } + // x && (a = x) + (AstKind::LogicalExpression(expr), _) => { + if expr.left.span().contains_inclusive(ref_span()) + && expr.right.get_inner_expression().is_assignment() + { + return false; + } + } + // x instanceof Foo && (a = x) + (AstKind::BinaryExpression(expr), _) if expr.operator.is_relational() => { + if expr.left.span().contains_inclusive(ref_span()) + && expr.right.get_inner_expression().is_assignment() + { + return false; + } + continue; + } (parent, AstKind::SequenceExpression(seq)) => { debug_assert!( !seq.expressions.is_empty(),