Fix nil pointer panic in FormatOnEnter
Fixes panic when pressing Enter in TypeScript files. The panic occurred when deriveActualIndentationFromList accessed nil nodes in AST node lists.
Root Cause
The stack trace showed the panic was in getStartLineAndCharacterForNode, but the actual issue was in deriveActualIndentationFromList where list.Nodes[index] or list.Nodes[i] can be nil. The AST implementation allows nil nodes in node lists (as confirmed by NodeIsMissing checking for nil), especially when parsing incomplete or malformed code.
Changes
- Added nil checks in
deriveActualIndentationFromList:- Check if
list.Nodes[index]is nil before using it (return -1) - Check if
list.Nodes[i]is nil in the loop before accessing properties (continue to next iteration)
- Check if
- Added test coverage:
TestFormatOnEnter_NilNodesInListvalidates the fix with various edge cases including empty files, incomplete code, and malformed syntax
func deriveActualIndentationFromList(list *ast.NodeList, index int, sourceFile *ast.SourceFile, options *FormatCodeSettings) int {
debug.Assert(list != nil && index >= 0 && index < len(list.Nodes))
node := list.Nodes[index]
if node == nil {
return -1
}
// ... rest of function
for i := index; i >= 0; i-- {
if list.Nodes[i] == nil {
continue
}
// ... rest of loop
}
}
- Fixes microsoft/typescript-go#2042
Original prompt
This section details on the original issue you should resolve
<issue_title>panic handling request textDocument/onTypeFormatting</issue_title> <issue_description>## Stack trace
panic handling request textDocument/onTypeFormatting runtime error: invalid memory address or nil pointer dereference goroutine 15626 [running]: runtime/debug.Stack() runtime/debug/stack.go:26 +0x5e github.com/microsoft/typescript-go/internal/lsp.(*Server).recover(0xc00019adc0, 0xc01a052ae0) github.com/microsoft/typescript-go/internal/lsp/server.go:571 +0x58 panic({0xb5c6a0?, 0x14bd140?}) runtime/panic.go:783 +0x132 github.com/microsoft/typescript-go/internal/ast.(*Node).Pos(...) github.com/microsoft/typescript-go/internal/ast/ast.go:240 github.com/microsoft/typescript-go/internal/scanner.GetTokenPosOfNode(0x88e?, 0xc017b961a0?, 0x8?) github.com/microsoft/typescript-go/internal/scanner/scanner.go:2324 +0xb2 github.com/microsoft/typescript-go/internal/format.getStartLineAndCharacterForNode(0xc006fddd80?, 0xc01e97c608) github.com/microsoft/typescript-go/internal/format/indent.go:250 +0x1a github.com/microsoft/typescript-go/internal/format.getIndentationForNodeWorker(0xebdd90?, 0xc01e97c608?, 0x7fe?, 0xc0081a7d38, 0xebf7a8?, 0xc01e97c608, 0x0, 0xc01653f1f0) github.com/microsoft/typescript-go/internal/format/indent.go:69 +0x1b0 github.com/microsoft/typescript-go/internal/format.GetIndentationForNode(0xc006fddd80, 0xc0081a7d38, 0xc01e97c608, 0xc01653f1f0) github.com/microsoft/typescript-go/internal/format/indent.go:17 +0x69 github.com/microsoft/typescript-go/internal/format.FormatSpan({0xebf7a8, 0xc01370f4a0}, {0x81b, 0x833}, 0xc01e97c608, 0x2) github.com/microsoft/typescript-go/internal/format/api.go:69 +0x105 github.com/microsoft/typescript-go/internal/format.FormatOnEnter({0xebf7a8, 0xc01370f4a0}, 0xc01e97c608, 0x0?) github.com/microsoft/typescript-go/internal/format/api.go:184 +0x20c github.com/microsoft/typescript-go/internal/ls.(*LanguageService).getFormattingEditsAfterKeystroke(0xc000e09248?, {0xebf7e0?, 0xc017d16c80?}, 0xc01e97c608, 0x0?, 0x833, {0xee2e30, 0x1}) github.com/microsoft/typescript-go/internal/ls/format.go:126 +0xf0 github.com/microsoft/typescript-go/internal/ls.(*LanguageService).ProvideFormatDocumentOnType(0xc01370f230, {0xebf7e0, 0xc017d16c80}, {0xc00a36a0e0?, 0x100000000000000?}, 0xc0096a41c0, {0xb4b2240?, 0xc0?}, {0xee2e30, 0x1}) github.com/microsoft/typescript-go/internal/ls/format.go:78 +0x119 github.com/microsoft/typescript-go/internal/lsp.(*Server).handleDocumentOnTypeFormat(0xc0003a2a20?, {0xebf7e0?, 0xc017d16c80?}, 0xc00a36a0e0?, 0x70?) github.com/microsoft/typescript-go/internal/lsp/server.go:889 +0x4a github.com/microsoft/typescript-go/internal/lsp.init.func1.registerLanguageServiceDocumentRequestHandler[...].22({0xebf7e0, 0xc017d16c80}, 0xc01a052ae0) github.com/microsoft/typescript-go/internal/lsp/server.go:557 +0x11a github.com/microsoft/typescript-go/internal/lsp.(*Server).handleRequestOrNotification(0xc00019adc0, {0xebf7e0, 0xc017d16c80}, 0xc01a052ae0) github.com/microsoft/typescript-go/internal/lsp/server.go:451 +0x10d github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop.func1() github.com/microsoft/typescript-go/internal/lsp/server.go:356 +0x3a created by github.com/microsoft/typescript-go/internal/lsp.(*Server).dispatchLoop in goroutine 19 github.com/microsoft/typescript-go/internal/lsp/server.go:376 +0x9adSteps to reproduce
- In a new project Just open a .ts/tsx file and press enter.
On latest preview build (7.0.0-dev.20251108.1), i never got this to work on any previous version, i know a similar issue was closed, but it didn't fix anything for me.</issue_description>
<agent_instructions>Create a failing test first, commit it, then fix the issue.</agent_instructions>
Comments on the Issue (you are @copilot in this section)
- Fixes microsoft/typescript-go#2042
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.