Skip to content

Conversation

camc314
Copy link
Contributor

@camc314 camc314 commented Aug 29, 2025

The parser was panicking when encountering JSDoc @satisfies tags on module.exports statements. The issue was that KindExportAssignment and KindJSExportAssignment were incorrectly grouped with nodes that have an Initializer() method, but they actually use Expression() instead.

Also added missing support for KindCommonJSExport in the Initializer() and SetInitializer() methods.

Reproduction:

/**
 * @satisfies {import('../types.js').SupportVersionTraceMap}
 */
module.exports = {
    zlib: zlib,
    'node:zlib': {
        ...zlib,
        [READ]: { supported: ['14.13.1', '12.20.0'] },
    },
};

I couldn't actually work out how to write a test for this, so would appreciate some guidance. thanks

@Copilot Copilot AI review requested due to automatic review settings August 29, 2025 09:32
…ments

The parser was panicking when encountering JSDoc `@satisfies` tags on
module.exports statements. The issue was that KindExportAssignment and
KindJSExportAssignment were incorrectly grouped with nodes that have
an Initializer() method, but they actually use Expression() instead.

Also added missing support for KindCommonJSExport in the Initializer()
and SetInitializer() methods.

Reproduction:
```javascript
/**
 * @Satisfies {import('../types.js').SupportVersionTraceMap}
 */
module.exports = {
    zlib: zlib,
    'node:zlib': {
        ...zlib,
        [READ]: { supported: ['14.13.1', '12.20.0'] },
    },
};
```
Copilot

This comment was marked as outdated.

@camc314 camc314 force-pushed the c/fix-export-assignment-initializer-panic branch from a77e847 to fd02030 Compare August 29, 2025 09:32
@jakebailey
Copy link
Member

To test, you'd want to make a compiler test in testdata/tests; just double check that it fails before the PR.

Adds a compiler test that verifies the parser doesn't panic when
encountering JSDoc @Satisfies tags on module.exports statements.
This test would have failed before the fix in the previous commit.
@camc314
Copy link
Contributor Author

camc314 commented Aug 29, 2025

thanks as always @jakebailey 🙂

Before/after
$ git revert fd020309cd50016dd45d5ee364918c4427c451dc
[c/fix-export-assignment-initializer-panic bd5ea5632] Revert "fix(parser): panic when parsing JSDoc `@satisfies` with export assignments"
 2 files changed, 2 insertions(+), 7 deletions(-)
$ go test -run TestLocal/panicSatisfiesOnExportEqualsDeclaration ./internal/testrunner -v
=== RUN   TestLocal
=== PAUSE TestLocal
=== CONT  TestLocal
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts
=== PAUSE TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts
=== CONT  TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts
    testutil.go:33: Panic on compiling test /Users/cameron/github/microsoft/typescript-go/testdata/tests/cases/compiler/panicSatisfiesOnExportEqualsDeclaration.ts:
        Unhandled case in Node.Initializer
        goroutine 5 [running]:
        runtime/debug.Stack()
                /opt/homebrew/Cellar/go/1.25.0/libexec/src/runtime/debug/stack.go:26 +0x64
        github.com/microsoft/typescript-go/internal/testutil.RecoverAndFail(0x14000003a40, {0x140000327e0, 0x8e})
                /Users/cameron/github/microsoft/typescript-go/internal/testutil/testutil.go:32 +0x44
        panic({0x105f7f5a0?, 0x10616e6f0?})
                /opt/homebrew/Cellar/go/1.25.0/libexec/src/runtime/panic.go:783 +0x120
        github.com/microsoft/typescript-go/internal/ast.(*Node).Initializer(0x14000298301?)
                /Users/cameron/github/microsoft/typescript-go/internal/ast/ast.go:783 +0x228
        github.com/microsoft/typescript-go/internal/parser.(*Parser).reparseHosted(0x140002b2708, 0x140003fd860, 0x140000a4c00, 0x14000d87880)
                /Users/cameron/github/microsoft/typescript-go/internal/parser/reparser.go:362 +0x12f8
        github.com/microsoft/typescript-go/internal/parser.(*Parser).reparseTags(0x140002b2708, 0x140000a4c00, {0x14000e7e920, 0x1, 0x1060a3440?})
                /Users/cameron/github/microsoft/typescript-go/internal/parser/reparser.go:64 +0x138
        github.com/microsoft/typescript-go/internal/parser.(*Parser).reparseCommonJS(0x140002b2708, 0x14000d87780?, {0x14000e7e920, 0x1, 0x1})
                /Users/cameron/github/microsoft/typescript-go/internal/parser/reparser.go:45 +0x2a8
        github.com/microsoft/typescript-go/internal/parser.(*Parser).parseExpressionOrLabeledStatement(0x140002b2708)
                /Users/cameron/github/microsoft/typescript-go/internal/parser/parser.go:1397 +0x190
        github.com/microsoft/typescript-go/internal/parser.(*Parser).parseStatement(0x140002b2708)
                /Users/cameron/github/microsoft/typescript-go/internal/parser/parser.go:983 +0x464
        github.com/microsoft/typescript-go/internal/parser.(*Parser).parseToplevelStatement(0x140002b2708, 0x2)
                /Users/cameron/github/microsoft/typescript-go/internal/parser/parser.go:384 +0x28
        github.com/microsoft/typescript-go/internal/parser.(*Parser).parseListIndex(0x140002b2708, 0x0, 0x10616c3d8)
                /Users/cameron/github/microsoft/typescript-go/internal/parser/parser.go:497 +0xe8
        github.com/microsoft/typescript-go/internal/parser.(*Parser).parseSourceFileWorker(0x140002b2708)
                /Users/cameron/github/microsoft/typescript-go/internal/parser/parser.go:336 +0x70
        github.com/microsoft/typescript-go/internal/parser.ParseSourceFile({{0x1400029a4b0, 0x30}, {0x1400029a4b0, 0x30}, {0x0, 0x0, 0x0, 0x0}, {0x0, 0x0}, ...}, ...)
                /Users/cameron/github/microsoft/typescript-go/internal/parser/parser.go:119 +0x118
        github.com/microsoft/typescript-go/internal/testutil/harnessutil.(*cachedCompilerHost).GetSourceFile(0x1400029a4b0?, {{0x1400029a4b0, 0x30}, {0x1400029a4b0, 0x30}, {0x0, 0x0, 0x0, 0x0}, {0x0, ...}, ...})
                /Users/cameron/github/microsoft/typescript-go/internal/testutil/harnessutil/harnessutil.go:514 +0x198
        github.com/microsoft/typescript-go/internal/compiler.(*fileLoader).parseSourceFile(0x140001d3808, 0x1400029c7e0)
                /Users/cameron/github/microsoft/typescript-go/internal/compiler/fileloader.go:421 +0x150
        github.com/microsoft/typescript-go/internal/compiler.(*parseTask).load(0x1400029c7e0, 0x140001d3808)
                /Users/cameron/github/microsoft/typescript-go/internal/compiler/filesparser.go:77 +0x158
        github.com/microsoft/typescript-go/internal/compiler.(*filesParser).start.func1()
                /Users/cameron/github/microsoft/typescript-go/internal/compiler/filesparser.go:220 +0x170
        github.com/microsoft/typescript-go/internal/core.(*singleThreadedWorkGroup).RunAndWait(0x140002893e0)
                /Users/cameron/github/microsoft/typescript-go/internal/core/workgroup.go:76 +0x54
        github.com/microsoft/typescript-go/internal/compiler.(*filesParser).parse(0x140002439a0, 0x14000022e10?, {0x1400024d020?, 0x14000289410?, 0x14000243ae0?})
                /Users/cameron/github/microsoft/typescript-go/internal/compiler/filesparser.go:176 +0x3c
        github.com/microsoft/typescript-go/internal/compiler.processAllProgramFiles({{0x106179608, 0x1400000ca20}, 0x1400029c5a0, 0x0, 0x2, 0x0, {0x0, 0x0}, {0x0, 0x0}, ...}, ...)
                /Users/cameron/github/microsoft/typescript-go/internal/compiler/fileloader.go:132 +0x5c4
        github.com/microsoft/typescript-go/internal/compiler.NewProgram({{0x106179608, 0x1400000ca20}, 0x1400029c5a0, 0x0, 0x2, 0x0, {0x0, 0x0}, {0x0, 0x0}, ...})
                /Users/cameron/github/microsoft/typescript-go/internal/compiler/program.go:207 +0x114
        github.com/microsoft/typescript-go/internal/testutil/harnessutil.createProgram({0x106179608, 0x1400000ca20}, 0x1400029c5a0)
                /Users/cameron/github/microsoft/typescript-go/internal/testutil/harnessutil/harnessutil.go:929 +0x98
        github.com/microsoft/typescript-go/internal/testutil/harnessutil.compileFilesWithHost({0x106179608?, 0x1400000ca20?}, 0x1400029c5a0, 0x14000287860)
                /Users/cameron/github/microsoft/typescript-go/internal/testutil/harnessutil/harnessutil.go:676 +0x30
        github.com/microsoft/typescript-go/internal/testutil/harnessutil.CompileFilesEx(0x14000003a40, {0x140002414e0, 0x2, 0x2}, {0x0, 0x0, 0x0}, 0x14000287860, 0x1400029f088, {0x140001f1f90, ...}, ...)
                /Users/cameron/github/microsoft/typescript-go/internal/testutil/harnessutil/harnessutil.go:218 +0xce4
        github.com/microsoft/typescript-go/internal/testutil/harnessutil.CompileFiles(0x14000003a40, {0x140002414e0, 0x2, 0x2}, {0x0, 0x0, 0x0}, 0x14000226810, 0x0, {0x140001f1f90, ...}, ...)
                /Users/cameron/github/microsoft/typescript-go/internal/testutil/harnessutil/harnessutil.go:107 +0x1c8
        github.com/microsoft/typescript-go/internal/testrunner.newCompilerTest(0x14000003a40, {0x1400003564c, 0x2a}, {0x14000035600, 0x76}, 0x14000179e68, 0x1400000c468)
                /Users/cameron/github/microsoft/typescript-go/internal/testrunner/compiler_runner.go:314 +0x708
        github.com/microsoft/typescript-go/internal/testrunner.(*CompilerBaselineRunner).runSingleConfigTest(0x1400007d500, 0x14000003a40, {0x1400003564c, 0x2a}, 0x14000200b40, 0x1400000c468)
                /Users/cameron/github/microsoft/typescript-go/internal/testrunner/compiler_runner.go:183 +0xfc
        github.com/microsoft/typescript-go/internal/testrunner.(*CompilerBaselineRunner).runTest.func1(0x14000003a40?)
                /Users/cameron/github/microsoft/typescript-go/internal/testrunner/compiler_runner.go:171 +0x30
        testing.tRunner(0x14000003a40, 0x14000226840)
                /opt/homebrew/Cellar/go/1.25.0/libexec/src/testing/testing.go:1934 +0xc8
        created by testing.(*T).Run in goroutine 4
                /opt/homebrew/Cellar/go/1.25.0/libexec/src/testing/testing.go:1997 +0x364
--- FAIL: TestLocal (0.01s)
    --- FAIL: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts (0.03s)
FAIL
FAIL    github.com/microsoft/typescript-go/internal/testrunner  0.382s
FAIL
$ git reset HEAD~1 --hard
HEAD is now at 863b6b7f3 test: add test case for JSDoc @satisfies with export assignments
$ go test -run TestLocal/panicSatisfiesOnExportEqualsDeclaration ./internal/testrunner -v
=== RUN   TestLocal
=== PAUSE TestLocal
=== CONT  TestLocal
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts
=== PAUSE TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts
=== CONT  TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/error
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/output
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/sourcemap
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/sourcemap_record
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/type
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/symbol
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/union_ordering
=== RUN   TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/source_file_parent_pointers
--- PASS: TestLocal (0.01s)
    --- PASS: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts (0.04s)
        --- PASS: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/error (0.00s)
        --- PASS: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/output (0.00s)
        --- PASS: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/sourcemap (0.00s)
        --- PASS: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/sourcemap_record (0.00s)
        --- PASS: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/type (0.00s)
        --- PASS: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/symbol (0.00s)
        --- PASS: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/union_ordering (0.00s)
        --- PASS: TestLocal/panicSatisfiesOnExportEqualsDeclaration.ts/source_file_parent_pointers (0.00s)
PASS
ok      github.com/microsoft/typescript-go/internal/testrunner  0.419s

@camc314 camc314 requested review from Copilot and jakebailey August 29, 2025 18:55
Copy link
Contributor

@Copilot 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

This PR fixes a parser panic that occurred when encountering JSDoc @satisfies tags on module.exports statements. The issue was caused by incorrect grouping of export assignment nodes with nodes that have an Initializer() method, when they actually use Expression() instead.

  • Moves KindExportAssignment and KindJSExportAssignment from the Initializer() case to the Expression() case in the JSDoc satisfies parser logic
  • Adds missing support for KindCommonJSExport in the Initializer() and SetInitializer() methods
  • Includes a test case to verify the fix works correctly

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
testdata/tests/cases/compiler/panicSatisfiesOnExportEqualsDeclaration.ts Test case demonstrating JSDoc @Satisfies on module.exports
testdata/baselines/reference/compiler/panicSatisfiesOnExportEqualsDeclaration.types Expected type output for the test case
testdata/baselines/reference/compiler/panicSatisfiesOnExportEqualsDeclaration.symbols Expected symbol output for the test case
internal/parser/reparser.go Fixes the parser logic by moving export assignments to the correct case
internal/ast/ast.go Adds missing KindCommonJSExport support in Initializer methods

@jakebailey jakebailey added this pull request to the merge queue Sep 3, 2025
Merged via the queue into microsoft:main with commit 1e359e7 Sep 3, 2025
22 checks passed
zshannon pushed a commit to zshannon/typescript-go that referenced this pull request Oct 6, 2025
zshannon pushed a commit to zshannon/typescript-go that referenced this pull request Oct 6, 2025
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.

2 participants