Skip to content

fix(oxlint): skip js-combine-iterations on lazy Iterator helper chains (#205)#272

Merged
aidenybai merged 6 commits into
mainfrom
cursor/lazy-iterator-chains-2fed
May 16, 2026
Merged

fix(oxlint): skip js-combine-iterations on lazy Iterator helper chains (#205)#272
aidenybai merged 6 commits into
mainfrom
cursor/lazy-iterator-chains-2fed

Conversation

@aidenybai
Copy link
Copy Markdown
Member

@aidenybai aidenybai commented May 16, 2026

Summary

Fixes the false positive in js-combine-iterations for chains rooted in lazy Iterator helpers. Previously, this benign single-pass pipeline:

const oddDoubles = numbers
  .values()
  .filter((value) => value % 2 === 1)
  .map((value) => 2 * value)
  .toArray();

…was reported as .filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of. Iterator helpers are lazy and single-pass, so this is incorrect.

Closes #205. Supersedes #212 (which only inspected the immediate inner-call receiver and missed deeper nesting, generators, Iterator.from, Object.values/.toArray() distinctions, etc.).

Approach

Walk the chain inward from the inner call's receiver, classifying each CallExpression:

  • Iterator-producing root → skip the rule:
    • <x>.values(), <x>.keys(), <x>.entries() where <x> is not the global Object (Maps, Sets, arrays, URLSearchParams, etc.)
    • Iterator.from(<x>)
    • Identifier callees that resolve to a syntactically-declared generator in the same file (function* gen() {} or const gen = function*() {}, including hoisted use)
  • Pass-through → keep walking: any chainable iteration method (map / filter / flatMap / forEach).
  • Materializing or unknown → stop walking, leave the rule firing: .toArray(), Array.from(...), plain identifiers, and any other call shape — so eager array chains and Iterator helper chains that have been re-materialized into arrays still get flagged.

Object.values/keys/entries(...) is intentionally treated as eager (returns an array), so Object.values(map).map().filter() still fires.

Cross-file imports of generators, class-method generators, and TS-type-only IterableIterator<T> parameters remain accepted false positives — documented in the test file.

Files

  • packages/oxlint-plugin-react-doctor/src/plugin/constants/js.ts — adds ITERATOR_PRODUCING_METHOD_NAMES.
  • packages/oxlint-plugin-react-doctor/src/plugin/rules/js-performance/js-combine-iterations.ts — collects per-file generator names on Program enter, then walks the receiver chain to detect iterator-rooted lazy chains before reporting.
  • packages/react-doctor/tests/regressions/js-performance-rules.test.ts — 18 new regression cases.

Test matrix (regression tests)

Input Expectation
arr.filter().map() flagged (eager)
numbers.values().filter().map().toArray() (issue #205 repro) not flagged
numbers.values().map().filter() (deeper nesting) not flagged
mapInstance.entries().map().filter() not flagged
setInstance.keys().filter().map() not flagged
Object.values(obj).map().filter() flagged (Object.* is eager)
Object.entries(obj).filter().map() flagged
arr.values().toArray().filter().map() flagged (.toArray() materializes)
Array.from(it).filter().map() flagged
Iterator.from(arr).map().filter() not flagged
Hoisted function* countUp(){} then countUp().filter().map() not flagged
const gen = function*(){}; gen().filter().map() not flagged
arr?.values()?.filter()?.map() (optional chaining) not flagged
groups.flatMap(...).filter().map() (eager .flatMap) flagged (both adjacent pairs)
groups.values().flatMap(...).filter().map().toArray() not flagged
arr.map(x => x ? x : null).filter(Boolean) not flagged (existing exclusion preserved)
arr.map(x => x ? x : null).filter(name => name) not flagged (existing identity-filter exclusion preserved)
Imported gen() from another file then .filter().map() flagged (documented limitation)

Verification

  • pnpm typecheck — green (10/10 tasks).
  • pnpm lint — 0 warnings, 0 errors across 542 files.
  • pnpm format — clean.
  • pnpm test998 / 998 tests pass, including all 18 new regression cases under js-combine-iterations.
  • pnpm --filter oxlint-plugin-react-doctor gen:check — registry in sync.

Eval Results

Ran against 100-repo corpus (baseline: main @ 529015d, 13,279 diagnostics).

Parity: OK — Zero regressions, zero changes. The fix correctly targets lazy iterator chains without affecting any other diagnostics across the entire corpus.

Metric Value
Diagnostics added 0
Diagnostics removed 0
Net change 0
Repos changed 0
Open in Web Open in Cursor 

cursoragent and others added 4 commits May 16, 2026 08:02
Walks the chain inward from the inner call's receiver, classifying each
CallExpression as iterator-producing (.values/.keys/.entries on a non-Object
receiver, Iterator.from(), or a syntactically-declared generator), pass-
through (chainable iteration method), or unknown/materializing. Lazy chains
rooted in iterators (e.g. arr.values().filter().map().toArray()) no longer
trip the rule, while eager array chains (and Object.values/keys/entries,
Array.from, .toArray() materializers) still do.

Closes #205.

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
…hains

Adds 18 test cases covering eager arrays (still flagged), .values/.keys/.entries
on Map/Set/array (not flagged), Object.values/entries (still flagged because
they return arrays), .toArray() / Array.from() materialization (still flagged),
Iterator.from(...) chains, hoisted and const-bound generator declarations,
optional chaining, .flatMap() walks, the existing Boolean / identity filter
exclusions, and a documented imported-generator false positive.

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
@reactreview
Copy link
Copy Markdown

reactreview Bot commented May 16, 2026

🔴 React Review0/100 (unchanged) · No new issues

Reviewed by react-review for commit 20238e5. Configure here.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-doctor-website Ready Ready Preview, Comment May 16, 2026 8:17am

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
…r fix"

This reverts commit b1c0d8e.

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
@aidenybai aidenybai marked this pull request as ready for review May 16, 2026 09:36
@aidenybai aidenybai merged commit d821ca2 into main May 16, 2026
7 checks passed
@aidenybai aidenybai deleted the cursor/lazy-iterator-chains-2fed branch May 16, 2026 11:23
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.

False positive on Js combine iterations

2 participants