Skip to content

Commit 2b933a9

Browse files
committed
fix(prefer-optional-chain): avoid access comparison false positive (#914)
Prevents `prefer-optional-chain` from reporting access-vs-access comparisons like `x && y && x.a === y.a`, where optional chaining would change when the other side is evaluated. Adds a regression test for the valid guarded comparison case; fixes oxc-project/oxc#21849.
1 parent f9cb483 commit 2b933a9

2 files changed

Lines changed: 15 additions & 14 deletions

File tree

internal/rules/prefer_optional_chain/prefer_optional_chain.go

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,14 +1533,11 @@ outer3:
15331533
leftIsAccess := utils.IsAccessExpression(left)
15341534
rightIsAccess := utils.IsAccessExpression(right)
15351535

1536-
// If both sides are access expressions with the same base, we can't convert
1537-
// to optional chaining (e.g., foo && foo.bar === foo.baz)
1536+
// If both sides are access expressions, we can't convert to optional
1537+
// chaining without changing when the other side is evaluated.
1538+
// For example, `x && y && x.a === y.a` must keep both guards.
15381539
if leftIsAccess && rightIsAccess {
1539-
leftBase := getBaseIdentifier(left)
1540-
rightBase := getBaseIdentifier(right)
1541-
if areNodesStructurallyEqual(leftBase, rightBase) {
1542-
return Operand{typ: OperandTypeInvalid, node: node}
1543-
}
1540+
return Operand{typ: OperandTypeInvalid, node: node}
15441541
}
15451542

15461543
comparedExpr := left
@@ -1578,14 +1575,10 @@ outer3:
15781575
leftIsAccess := utils.IsAccessExpression(left)
15791576
rightIsAccess := utils.IsAccessExpression(right)
15801577

1581-
// If both sides are access expressions with the same base, we can't convert
1582-
// to optional chaining (e.g., foo == null || foo.bar === foo.baz)
1578+
// If both sides are access expressions, we can't convert to optional
1579+
// chaining without changing when the other side is evaluated.
15831580
if leftIsAccess && rightIsAccess {
1584-
leftBase := getBaseIdentifier(left)
1585-
rightBase := getBaseIdentifier(right)
1586-
if areNodesStructurallyEqual(leftBase, rightBase) {
1587-
return Operand{typ: OperandTypeInvalid, node: node}
1588-
}
1581+
return Operand{typ: OperandTypeInvalid, node: node}
15891582
}
15901583

15911584
comparedExpr := left

internal/rules/prefer_optional_chain/prefer_optional_chain_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,14 @@ if (!a || b === a || b.indexOf('input, button, a')) {
10081008
console.log('Hello, World');
10091009
}`,
10101010
},
1011+
{Code: `
1012+
function test() {
1013+
let x: undefined | { a: string }, y: undefined | { a: string }
1014+
if (x && y && x.a === y.a) {
1015+
console.log("x and y are equal")
1016+
}
1017+
}
1018+
`},
10111019
}
10121020

10131021
invalidCases := []rule_tester.InvalidTestCase{

0 commit comments

Comments
 (0)