typescript-go icon indicating copy to clipboard operation
typescript-go copied to clipboard

Fix nil pointer panic in FormatOnEnter

Open Copilot opened this issue 1 month ago • 2 comments

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)
  • Added test coverage: TestFormatOnEnter_NilNodesInList validates 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 +0x9ad

Steps to reproduce

  1. 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.

Copilot avatar Nov 08 '25 16:11 Copilot