Skip to content

fix: iterateAllJsdocs free comments after each file#1706

Merged
brettz9 merged 1 commit into
gajus:mainfrom
overlookmotel:tracked-jsdocs-leak
Jun 17, 2026
Merged

fix: iterateAllJsdocs free comments after each file#1706
brettz9 merged 1 commit into
gajus:mainfrom
overlookmotel:tracked-jsdocs-leak

Conversation

@overlookmotel

@overlookmotel overlookmotel commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Problem

iterateAllJsdocs declared trackedJsdocs (a Set used to dedupe JSDoc comments already visited) in the closure that is created once when the rule is defined. This Set is shared across all files. Prior to this PR, the set was only ever added to, and nothing cleared it between files.

Every JSDoc comment attached to a node gets added to the Set and never removed, so the Set grows for the lifetime of the lint process, preventing comment objects from all files from being garbage-collected.

Additionally, comment objects' value is a slice of the file's source text, so the full source of every linted file containing a JSDoc comment also stays referenced and can't be GCed.

In LSP context, the Set's growth is even more unbounded - it would live for the length of the LSP process, and continue growing every time a file is linted.

Probably less important to you, but... This behavior also breaks eslint-plugin-jsdoc running as a JS plugin in Oxlint. For performance reasons, Oxlint recycles comment objects (re-uses the same objects for each file), so this caused rules to malfunction - trackedJsdocs.has(node) would sometimes return true because that comment object was already put in the Set in a previous file (oxc-project/oxc#23477).

Rules affected

All rules that run via the iterateAllJsdocs: true path - 39 rules, including check-tag-names, check-alignment, no-undefined-types, require-template, sort-tags, multiline-blocks, and the ts-* rules. The 'any'-context path that also calls iterateAllJsdocs was already creating a fresh instance per file and was not affected.

Fix

Move the const trackedJsdocs = new Set(); declaration into create(), so each file gets its own Set that's discarded when the file's lint pass finishes.

No behaviour change - just fixes the leak.

Alternative considered

I considered instead adding trackedJsdocs.clear() to Program:exit, but feel that's a less robust solution. If some rule throws an error during AST traversal (any rule in user's config, not necessarily one of eslint-plugin-jsdoc's), Program:exit will not fire, which in LSP would leave stale state in the Set which survives until the next lint run. That's less problematic for ESLint, but would bite more heavily in Oxlint.

In terms of performance, clear() would be no more efficient - in V8 at least, clear() deallocates the Set's backing store and creates a fresh one (so Claude tells me), so it doesn't avoid an allocation vs the solution in this PR.

Copilot AI review requested due to automatic review settings June 16, 2026 18:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adjusts where trackedJsdocs is initialized within iterateAllJsdocs, likely to better align the tracking lifetime with the returned visitor/state lifecycle.

Changes:

  • Moves trackedJsdocs initialization from the top of iterateAllJsdocs to just before state/return creation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@brettz9 brettz9 force-pushed the tracked-jsdocs-leak branch from 3e13f4e to 6c3126a Compare June 17, 2026 02:52
@brettz9 brettz9 merged commit ebe0d08 into gajus:main Jun 17, 2026
5 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 63.0.6 🎉

The release is available on:

Your semantic-release bot 📦🚀

@brettz9

brettz9 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants