fix(project): release stale retainingProjects entries when program drops a reference#3992
Closed
Zelys-DFKH wants to merge 1 commit into
Closed
fix(project): release stale retainingProjects entries when program drops a reference#3992Zelys-DFKH wants to merge 1 commit into
Zelys-DFKH wants to merge 1 commit into
Conversation
… a program When updateProgram rebuilds a project's program after a project reference is removed from tsconfig, the dropped reference's retainingProjects entry was never released. Later, deleteConfiguredProject only releases the current program's references, so the stale entry persisted into the next snapshot. When that referenced config changed, markProjectsAffectedByConfigChanges panicked because the deleted project appeared in affectedProjects but was no longer in configuredProjects. Fix: before rebuilding, release all of the old program's project references. The new program re-acquires whatever it still needs via GetResolvedProjectReference. Fixes microsoft#3942 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Fixes a panic in markProjectsAffectedByConfigChanges caused by stale retainingProjects entries when a project drops a reference during rebuild but is later deleted.
Changes:
- After
CreateProgram, diff old vs new program's resolved project references and release dropped ones fromconfigFileRegistryBuilder. - Extracted shared
releaseProjectReferenceshelper;deleteConfiguredProjectnow uses it. - Added regression test simulating reference removal, project deletion, then referenced-config change.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| internal/project/projectcollectionbuilder.go | Releases stale project reference retainers post-rebuild and refactors release logic into a helper. |
| internal/project/projectreferencesprogram_test.go | Adds regression test for the panic scenario described in #3942. |
Member
|
Closing automated PRs; please don't run automated coding tools against this repo. We're fully able to do that ourselves. |
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.
Fixes #3942
Thanks to @DanielRosenwasser for the stack trace, which made tracing the root cause much more direct.
Analysis
updateProgramcallsacquireConfigForProjectfor every project reference it resolves viaGetResolvedProjectReference, adding the current project's config path to each referenced config'sretainingProjects. When the program is rebuilt with fewer references (areferencesentry removed from tsconfig), only the new set is acquired. Nothing releases the entries that were in the old program but dropped from the new one.The dropped config ends up with a stale entry in
retainingProjects: the project path is present, but the project no longer treats that config as a reference. The stale entry is invisible during the rebuild because the project is still alive.The invariant breaks when the project is later deleted.
deleteConfiguredProjectcallsprogram.RangeResolvedProjectReferenceto release all references, but that iterates the current program, which does not include the dropped reference. The staleretainingProjectsentry survives deletion and persists into the finalized snapshot.In the next snapshot, if that referenced config changes,
configFileRegistryBuilder.DidChangeFilescopiesretainingProjectsintoaffectedProjectsand passes it tomarkProjectsAffectedByConfigChanges. That function loads the project path fromconfiguredProjectsand panics because the project was already deleted.The
scheduleIdleCacheCleantimer is a common trigger. It fires after the project is already gone, carrying accumulated file changes that may include a tsconfig modification.Fix
After
CreateProgramcompletes, diff the old program's project references against the new program's and release any absent from the new one. This is safer than releasing all old refs eagerly before the rebuild: on incremental updates (single dirty file, same command line),CreateProgramclones the old program viaUpdateProgramwithout callingGetResolvedProjectReference, so the new program's reference set is identical to the old one. An eager release in that path would remove the project from every dependency'sretainingProjects, causing config changes in those dependencies to stop marking this project dirty.The post-rebuild diff catches dropped references regardless of whether the rebuild was full or incremental. References the new program still holds remain untouched. The diff happens outside
entry.Locked, matching the existing pattern.deleteConfiguredProjectstill releases all references unconditionally on project deletion; the release logic lives in a sharedreleaseProjectReferenceshelper.updateProgramuses the post-rebuild diff instead.The regression test in
TestProjectReferencesProgramwalks the full sequence: open a file that causes a project reference to be acquired, rebuild after removing the reference, delete the project by closing the file, then change the formerly-referenced config. Without the fix this panics. With the fix it completes cleanly.PR #3987 addresses the same crash by replacing the panic in
markProjectsAffectedByConfigChangeswith a guarded skip. That's a reasonable defensive measure. This PR fixes the root cause instead, soretainingProjectsstays consistent and the panic legitimately never fires.Copilot Checklist
I successfully ran these commands at the end of my session, and they completed without error:
TestFindAllRefsDoesNotTryToSearchProjectAfterItsUpdateDoesNotIncludeTheFileandTestDeclarationMapsRenamefail with baseline drift; these also fail onorigin/mainwithout this change.go test ./internal/project/...passes cleanly.)