Skip to content

Commit a57e81c

Browse files
Copilotjakebailey
andcommitted
Fix nil pointer panic by handling nil nodes in deriveActualIndentationFromList
The panic in issue #2042 occurs when list.Nodes[index] is nil. Added nil checks in deriveActualIndentationFromList to properly handle nil nodes in AST node lists, which can occur during parsing of incomplete or malformed code. Co-authored-by: jakebailey <[email protected]>
1 parent 6cf70ba commit a57e81c

File tree

2 files changed

+16
-25
lines changed

2 files changed

+16
-25
lines changed

internal/format/format_test.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ func TestFormatNoTrailingNewline(t *testing.T) {
6161
}
6262

6363
// Test for panic handling request textDocument/onTypeFormatting (issue #2042)
64-
// The panic occurs when getStartLineAndCharacterForNode is called with a nil node
65-
func TestFormatOnEnter_NilNodeHandling(t *testing.T) {
64+
// The panic occurs when nodes in a list can be nil, causing deriveActualIndentationFromList
65+
// to panic when accessing node properties
66+
func TestFormatOnEnter_NilNodesInList(t *testing.T) {
6667
t.Parallel()
6768

68-
// Test various edge cases that could lead to nil nodes being passed
69-
// to getStartLineAndCharacterForNode
69+
// Test cases that can produce nil nodes in AST lists
7070
testCases := []struct {
7171
name string
7272
text string
@@ -82,20 +82,15 @@ func TestFormatOnEnter_NilNodeHandling(t *testing.T) {
8282
text: "const x = 1;",
8383
position: 12,
8484
},
85-
{
86-
name: "file with newline",
87-
text: "const x = 1;\n",
88-
position: 13,
89-
},
9085
{
9186
name: "incomplete code",
9287
text: "if (",
9388
position: 4,
9489
},
9590
{
96-
name: "malformed if-else",
97-
text: "if(a){}\nelse",
98-
position: 12,
91+
name: "malformed syntax",
92+
text: "function f() { return }",
93+
position: 21,
9994
},
10095
}
10196

@@ -117,7 +112,7 @@ func TestFormatOnEnter_NilNodeHandling(t *testing.T) {
117112
},
118113
}, "\n")
119114

120-
// This should not panic even with nil nodes
115+
// This should not panic even with nil nodes in lists
121116
edits := format.FormatOnEnter(ctx, sourceFile, tc.position)
122117
_ = edits // Just ensuring no panic
123118
})

internal/format/indent.go

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,19 @@ func deriveActualIndentationFromList(list *ast.NodeList, index int, sourceFile *
177177
debug.Assert(list != nil && index >= 0 && index < len(list.Nodes))
178178

179179
node := list.Nodes[index]
180+
if node == nil {
181+
return -1
182+
}
180183

181184
// walk toward the start of the list starting from current node and check if the line is the same for all items.
182185
// if end line for item [i - 1] differs from the start line for item [i] - find column of the first non-whitespace character on the line of item [i]
183186

184187
line, char := getStartLineAndCharacterForNode(node, sourceFile)
185188

186189
for i := index; i >= 0; i-- {
190+
if list.Nodes[i] == nil {
191+
continue
192+
}
187193
if list.Nodes[i].Kind == ast.KindCommaToken {
188194
continue
189195
}
@@ -242,9 +248,6 @@ func findFirstNonWhitespaceCharacterAndColumn(startPos int, endPos int, sourceFi
242248
func childStartsOnTheSameLineWithElseInIfStatement(parent *ast.Node, child *ast.Node, childStartLine int, sourceFile *ast.SourceFile) bool {
243249
if parent.Kind == ast.KindIfStatement && parent.AsIfStatement().ElseStatement == child {
244250
elseKeyword := astnav.FindPrecedingToken(sourceFile, child.Pos())
245-
if elseKeyword == nil {
246-
return false
247-
}
248251
debug.AssertIsDefined(elseKeyword)
249252
elseKeywordStartLine, _ := getStartLineAndCharacterForNode(elseKeyword, sourceFile)
250253
return elseKeywordStartLine == childStartLine
@@ -253,9 +256,6 @@ func childStartsOnTheSameLineWithElseInIfStatement(parent *ast.Node, child *ast.
253256
}
254257

255258
func getStartLineAndCharacterForNode(n *ast.Node, sourceFile *ast.SourceFile) (line int, character int) {
256-
if n == nil {
257-
return 0, 0
258-
}
259259
return scanner.GetECMALineAndCharacterOfPosition(sourceFile, scanner.GetTokenPosOfNode(n, sourceFile, false))
260260
}
261261

@@ -536,12 +536,8 @@ func childIsUnindentedBranchOfConditionalExpression(parent *ast.Node, child *ast
536536
// ? 1 : ( L1: whenTrue indented because it's on a new line
537537
// 0 L2: indented two stops, one because whenTrue was indented
538538
// ); and one because of the parentheses spanning multiple lines
539-
whenTrue := parent.AsConditionalExpression().WhenTrue
540-
if whenTrue == nil {
541-
return false
542-
}
543-
trueStartLine, _ := getStartLineAndCharacterForNode(whenTrue, sourceFile)
544-
trueEndLine, _ := scanner.GetECMALineAndCharacterOfPosition(sourceFile, whenTrue.End())
539+
trueStartLine, _ := getStartLineAndCharacterForNode(parent.AsConditionalExpression().WhenTrue, sourceFile)
540+
trueEndLine, _ := scanner.GetECMALineAndCharacterOfPosition(sourceFile, parent.AsConditionalExpression().WhenTrue.End())
545541
return conditionEndLine == trueStartLine && trueEndLine == childStartLine
546542
}
547543
}

0 commit comments

Comments
 (0)