fix: iterateAllJsdocs free comments after each file#1706
Merged
Conversation
Contributor
There was a problem hiding this comment.
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
trackedJsdocsinitialization from the top ofiterateAllJsdocsto just beforestate/returncreation.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3e13f4e to
6c3126a
Compare
|
🎉 This PR is included in version 63.0.6 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Collaborator
|
Thanks for the PR! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
iterateAllJsdocsdeclaredtrackedJsdocs(aSetused to dedupe JSDoc comments already visited) in the closure that is created once when the rule is defined. ThisSetis 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
Setand never removed, so theSetgrows for the lifetime of the lint process, preventing comment objects from all files from being garbage-collected.Additionally, comment objects'
valueis 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-jsdocrunning 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 returntruebecause that comment object was already put in theSetin a previous file (oxc-project/oxc#23477).Rules affected
All rules that run via the
iterateAllJsdocs: truepath - 39 rules, includingcheck-tag-names,check-alignment,no-undefined-types,require-template,sort-tags,multiline-blocks, and thets-*rules. The'any'-context path that also callsiterateAllJsdocswas already creating a fresh instance per file and was not affected.Fix
Move the
const trackedJsdocs = new Set();declaration intocreate(), so each file gets its ownSetthat's discarded when the file's lint pass finishes.No behaviour change - just fixes the leak.
Alternative considered
I considered instead adding
trackedJsdocs.clear()toProgram: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 ofeslint-plugin-jsdoc's),Program:exitwill not fire, which in LSP would leave stale state in theSetwhich 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 theSet'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.