Skip to content

Fix external helper crash after module-status updates#3994

Open
Copilot wants to merge 7 commits into
mainfrom
copilot/fix-check-external-emit-helpers-again
Open

Fix external helper crash after module-status updates#3994
Copilot wants to merge 7 commits into
mainfrom
copilot/fix-check-external-emit-helpers-again

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 19, 2026

checkExternalEmitHelpers could panic after an incremental program update when a file changed from script to module. The reused program state could lack the synthetic tslib import specifier needed for helper resolution.

  • Program reuse

    • Treat changes to ExternalModuleIndicator or CommonJSModuleIndicator as non-reusable.
    • Forces rebuilt module-resolution state when helper imports may need to be synthesized.
  • Regression coverage

    • Added a checker test for updating a file to an exported decorated class under importHelpers.
    • Verifies the updated program rebuilds synthetic tslib state and completes semantic diagnostics without errors.
declare function dec(value: Function): void;

@dec
export class C {}

Copilot AI and others added 3 commits May 19, 2026 21:11
Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix crash in checkExternalEmitHelpers Fix external helper crash after module-status updates May 19, 2026
Copilot AI requested a review from DanielRosenwasser May 19, 2026 21:13
@DanielRosenwasser
Copy link
Copy Markdown
Member

Perfect - can you write this as a fourslash test instead? Make sure the fourslash test actually fails without the fix.

@DanielRosenwasser
Copy link
Copy Markdown
Member

@copilot

Copilot AI and others added 3 commits May 19, 2026 21:59
Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 19, 2026

Perfect - can you write this as a fourslash test instead? Make sure the fourslash test actually fails without the fix.

Addressed in f732c93. I replaced the checker-level regression with a fourslash test and verified it fails without the production fix with the textDocument/diagnostic nil-pointer panic.

@DanielRosenwasser DanielRosenwasser marked this pull request as ready for review May 19, 2026 22:06
Copilot AI review requested due to automatic review settings May 19, 2026 22:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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.

Fixes an incremental-program reuse edge case that could crash external helper resolution when a file changes from script to module (e.g., via adding an export) under importHelpers.

Changes:

  • Mark program state as non-reusable when a file’s module-status indicators (ExternalModuleIndicator / CommonJSModuleIndicator) change.
  • Add a fourslash regression test that edits a script into an exported decorated module under importHelpers and verifies diagnostics remain error-free.
Show a summary per file
File Description
internal/fourslash/tests/importHelpersAfterScriptBecomesDecoratedModule_test.go Adds regression test covering script→decorated module transition with importHelpers.
internal/compiler/program.go Prevents incremental reuse when module-status indicators change, forcing rebuild of module-resolution/helper state.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 0

return file2 != nil &&
file1.ParseOptions() == file2.ParseOptions() &&
file1.UsesUriStyleNodeCoreModules == file2.UsesUriStyleNodeCoreModules &&
(file1.ExternalModuleIndicator != nil) == (file2.ExternalModuleIndicator != nil) &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Don't we need to check that they're equal in some way?

Copy link
Copy Markdown
Member

@DanielRosenwasser DanielRosenwasser May 20, 2026

Choose a reason for hiding this comment

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

I don't think so - my understanding is that these are used to determine if the program is sufficiently structurally the same to avoid rebuilding a full program - not that the files themselves are "equal" before/after. Below, we check that the rest of the structure is equal (e.g. same imports, global modifications, etc.)

From program.go's perspective, the external module indicator probably should not be used to determine anything about the file other than "is this a module, or is this a global?"

@DanielRosenwasser DanielRosenwasser requested a review from Copilot May 20, 2026 16:48
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.

Crash via checkExternalEmitHelpers

4 participants