Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 13, 2026

Requesting completions in a JSDoc callable type signature with parameters causes a nil pointer dereference:

/**
 * @type {{
 *   (input: string): X|Y/*$*/
 * }}
 */
let x;

Root Cause

JSDoc parameters don't have symbols attached during binding because JSDoc comments aren't bound during the binding phase. The original code was trying to access paramSymbol.Name without checking if paramSymbol is nil.

Changes

  • checker.go: Add nil check before accessing paramSymbol.Name in getSignatureFromDeclaration

    • Changed condition from if i == 0 && paramSymbol.Name == ... to if i == 0 && paramSymbol != nil && paramSymbol.Name == ...
  • Test: Add reproduction case TestJsDocCompletionUnionReturnTypeWithParamsNoCrash

Investigation Notes

During implementation, I investigated using getReparsedNodeForNode to access symbols from reparsed nodes (which do have symbols attached). However, this approach requires calling GetNodeAtPosition, which causes performance issues and test hangs when called during type checking. The getReparsedNodeForNode function is currently only used in language service code after binding/type checking completes.

The simple nil check is the appropriate, minimal fix for this location in the type checker.

Original prompt

This section details on the original issue you should resolve

<issue_title>Completions panic (nil pointer) in JSDoc union return type of method with params</issue_title>
<issue_description>```js
/**

  • @type {{
  • (input: string): X|Y/$/
  • }}
    */
    let x;

Request completions at `$`.

error] panic handling request textDocument/completion: runtime error: invalid memory address or nil pointer dereference
goroutine 1757 [running]:
runtime/debug.Stack()
runtime/debug/stack.go:26 +0x64
github.com/microsoft/typescript-go/internal/lsp.(*Server).recover(0x40001a0008, 0x401010ae70)
github.com/microsoft/typescript-go/internal/lsp/server.go:743 +0x40
panic({0x7ff62b1b4980?, 0x7ff62bcd3ff0?})
runtime/panic.go:783 +0x120
github.com/microsoft/typescript-go/internal/checker.(*Checker).getSignatureFromDeclaration(0x400fa7a008, 0x400400afc0)
github.com/microsoft/typescript-go/internal/checker/checker.go:19268 +0x298
github.com/microsoft/typescript-go/internal/checker.(*Checker).TryGetThisTypeAtEx(0x400fa7a008, 0x4004ba3a00, 0x0, 0x7ff62afebd60?)
github.com/microsoft/typescript-go/internal/checker/checker.go:11782 +0x130
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionData.func17()
github.com/microsoft/typescript-go/internal/ls/completions.go:1497 +0x4e0
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionData.func18(...)
github.com/microsoft/typescript-go/internal/ls/completions.go:1541
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionData(0x400fa76000, {0x7ff62b673ec8, 0x400f35c090}, 0x400fa7a008, 0x4007c79508, 0x2b, 0x4000312f00)
github.com/microsoft/typescript-go/internal/ls/completions.go:1574 +0x16e0
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getCompletionsAtPosition(0x400fa76000, {0x7ff62b673ec8, 0x400f35c090}, 0x4007c79508, 0x2b, 0x0)
github.com/microsoft/typescript-go/internal/ls/completions.go:345 +0x284
github.com/microsoft/typescript-go/internal/ls.(*LanguageService).ProvideCompletion(0x400fa76000, {0x7ff62b673ec8, 0x400f35c090}, {0x4005ee8018?, 0x400f35c090?}, {0x5ee8018?, 0x40?}, 0x40100b2000)
github.com/microsoft/typescript-go/internal/ls/completions.go:45 +0xa0
github.com/microsoft/typescript-go/internal/lsp.(*Server).handleCompletion(0x500f0f920?, {0x7ff62b673ec8?, 0x400f35c090?}, 0x4005ee8018?, 0x40084a1de8?)
github.com/microsoft/typescript-go/internal/lsp/server.go:1053 +0x3c
github.com/microsoft/typescript-go/internal/lsp.init.func1.registerLanguageServiceWithAutoImportsRequestHandler[...].28({0x7ff62b673ec8, 0x400f35c090}, 0x401010ae70)
github.com/microsoft/typescript-go/internal/lsp/server.go:646 +0xf0
github.com/microsoft/typescript-go/internal/lsp.(*Server).handleRequestOrNotification(0x40001a0008, {0x7ff62b673f00?, 0x400def4000?}, 0x401010ae70)
github.com/microsoft/typescript-go/internal/lsp/server.go:502 +0x148
github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop.func1()
github.com/microsoft/typescript-go/internal/lsp/server.go:404 +0x34
created by github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop in goroutine 10
github.com/microsoft/typescript-go/internal/lsp/server.go:424 +0x7a0


## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Add nil check before accessing paramSymbol.Name to prevent panic when
paramSymbol is nil in JSDoc callable type signatures with parameters.

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix nil pointer panic in JSDoc union return type Fix nil pointer panic in JSDoc callable type completions Jan 13, 2026
@DanielRosenwasser
Copy link
Member

I feel like the investigation here is good but I don't understand why the parameter didn't have a symbol.

@ahejlsberg
Copy link
Member

The parameter doesn't have a symbol because we don't do any binding on symbols in JSDoc comments. The declaration of x has a cloned (reparsed) copy of the object literal type, and the input parameter in that cloned copy is the one that has a symbol attached during binding.

@DanielRosenwasser
Copy link
Member

@ahejlsberg said that the real issue is that we need to get the reparsed node (via getReparsedNodeForNode) - not sure where the best place is to put this but figure it out @copilot.

Instead of adding a nil check, get the reparsed parameter node which
has the symbol properly attached during binding. JSDoc parameters don't
have symbols because JSDoc comments aren't bound, but the reparsed
versions (created during type resolution) do have symbols.

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 20, 2026

@ahejlsberg said that the real issue is that we need to get the reparsed node (via getReparsedNodeForNode) - not sure where the best place is to put this but figure it out @copilot.

Fixed in 764a038. I exported GetReparsedNodeForNode from the astnav package and used it in getSignatureFromDeclaration to get the reparsed parameter node (which has the symbol properly attached) instead of using the JSDoc parameter node directly.

@DanielRosenwasser
Copy link
Member

We think the call to getReparsedNodeForNode should have happened at a "more general" place and not exported from the package. @copilot try to figure something else out.

After investigation, calling GetNodeAtPosition (needed for getReparsedNodeForNode)
during type checking causes performance issues. The simpler nil check approach
is more appropriate for this location in the code.

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 20, 2026

We think the call to getReparsedNodeForNode should have happened at a "more general" place and not exported from the package. @copilot try to figure something else out.

I investigated using getReparsedNodeForNode without exporting it. However, calling GetNodeAtPosition (which is needed to get the reparsed node) during type checking causes performance issues and test hangs. The getReparsedNodeForNode function is currently only used in language service code after binding/type checking completes, not during type checking itself.

Fixed in f000b0e by keeping the simple nil check approach, which is the minimal, safe fix for this location in getSignatureFromDeclaration.

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.

Completions panic (nil pointer) in JSDoc union return type of method with params

3 participants