Skip to content

fix(prefer-optional-chain): avoid false positives for instanceof and redundant nullish checks#753

Open
wagenet wants to merge 1 commit intooxc-project:mainfrom
wagenet:fix-optional-chain
Open

fix(prefer-optional-chain): avoid false positives for instanceof and redundant nullish checks#753
wagenet wants to merge 1 commit intooxc-project:mainfrom
wagenet:fix-optional-chain

Conversation

@wagenet
Copy link
Contributor

@wagenet wagenet commented Feb 26, 2026

Summary

  • instanceof false positive: patterns like (!a.b || foo instanceof a.b) and (a.b && foo instanceof a.b) were incorrectly flagged. Added KindInstanceOfKeyword to the invalid-operator switches in both the AND-chain and OR-chain branches of parseOperandinstanceof has no optional-chain equivalent.

  • Redundant nullish-check false positive: a.b === null || a.b === undefined was falsely reported because both operands check the exact same expression with no deeper member access to convert. Added an allOperandsCheckSameExpression guard to validateOrChainForReporting, mirroring the identical guard already present in validateAndChainForReporting.

Test plan

  • (!a.b || foo instanceof a.b) — no violation
  • (a.b && foo instanceof a.b) — no violation
  • request.payload === null || request.payload === undefined — no violation
  • request.payload === undefined || request.payload === null — no violation
  • go test ./internal/rules/prefer_optional_chain/... passes

🤖 Generated with Claude Code

@camc314 camc314 marked this pull request as draft February 26, 2026 12:16
@camc314
Copy link
Contributor

camc314 commented Feb 26, 2026

@wagenet mind rebasing? thanks

@camc314 camc314 self-assigned this Feb 26, 2026
@kalvenschraut
Copy link
Contributor

kalvenschraut commented Feb 26, 2026

May want to double check main has these fixed, looks pretty similar to my other PR that was merged. AI wanted to add instanceof also but those weren't actually being triggered at least the ones I tried

#732

…f` and redundant nullish checks

Two false positives are fixed:

1. `in`/`instanceof` operators: expressions like `(!a.b || key in a.b)` or
   `(a.b && foo instanceof a.b)` were incorrectly flagged because `in` and
   `instanceof` have no optional-chain equivalent. Add `KindInKeyword` and
   `KindInstanceOfKeyword` to the invalid-operator switches in both the
   AND-chain and OR-chain branches of `parseOperand`.

2. Redundant nullish checks: `a.b === null || a.b === undefined` was falsely
   reported because the chain passed `hasPropertyAccessInChain` (a.b is an
   access expression) even though both operands check the exact same
   expression with no deeper member access to convert. Add an
   `allOperandsCheckSameExpression` guard to `validateOrChainForReporting`,
   mirroring the identical guard already present in
   `validateAndChainForReporting`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@wagenet wagenet changed the title fix(prefer-optional-chain): avoid false positives for in/instanceof and redundant nullish checks fix(prefer-optional-chain): avoid false positives for instanceof and redundant nullish checks Feb 26, 2026
@wagenet wagenet marked this pull request as ready for review February 26, 2026 17:01

// When every operand checks the exact same expression (e.g., a.b === null || a.b === undefined),
// there is no member access to convert — skip flagging, mirroring the AND-chain guard.
if processor.allOperandsCheckSameExpression(chain) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// so these patterns must never be reported.
validCases = append(validCases, rule_tester.ValidTestCase{
Code: `(!data.previous_values || key in data.previous_values)`,
}, rule_tester.ValidTestCase{
Copy link
Contributor

@kalvenschraut kalvenschraut Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cloned this file from this branch and all these test cases pass on main so I think my changes in #732 are sufficient

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh... that doesn't match what I was seeing. Guess we'll need to dig in more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was your main up to date? As my PR was recently merged/released

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I updated it after I rebased.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recording.2026-02-26.210044.mp4

above shows what I am doing at least 🤷 I started with your changes locally as shown by the diff and then show that i have no other changes and I am on main. Then I run the test and they still pass meaning no changes are required to satisfy these test cases. Unless I am overlooking something

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants