Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/ls/codeactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (l *LanguageService) ProvideCodeActions(ctx context.Context, params *lsprot
// Process diagnostics in the context to generate quick fixes
if params.Context != nil && params.Context.Diagnostics != nil {
for _, diag := range params.Context.Diagnostics {
if diag.Code.Integer == nil {
if diag.Code == nil || diag.Code.Integer == nil {
continue
}

Expand Down
91 changes: 91 additions & 0 deletions internal/ls/codeactions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package ls

import (
"testing"

"github.com/microsoft/typescript-go/internal/lsp/lsproto"
)

// Test for issue: panic handling request textDocument/codeAction
// This verifies that we safely handle nil diagnostic codes without panicking
func TestCodeActionDiagnosticNilChecks(t *testing.T) {
t.Parallel()

testCases := []struct {
name string
diagnostic *lsproto.Diagnostic
shouldSkip bool
}{
{
name: "nil Code field",
diagnostic: &lsproto.Diagnostic{
Range: lsproto.Range{
Start: lsproto.Position{Line: 0, Character: 0},
End: lsproto.Position{Line: 0, Character: 10},
},
Message: "Test diagnostic with nil code",
Code: nil,
},
shouldSkip: true,
},
{
name: "nil Integer in Code (string code instead)",
diagnostic: &lsproto.Diagnostic{
Range: lsproto.Range{
Start: lsproto.Position{Line: 0, Character: 0},
End: lsproto.Position{Line: 0, Character: 10},
},
Message: "Test diagnostic with string code",
Code: &lsproto.IntegerOrString{
String: stringPtr("TS1234"),
Integer: nil,
},
},
shouldSkip: true,
},
{
name: "valid Integer code",
diagnostic: &lsproto.Diagnostic{
Range: lsproto.Range{
Start: lsproto.Position{Line: 0, Character: 0},
End: lsproto.Position{Line: 0, Character: 10},
},
Message: "Test diagnostic with integer code",
Code: &lsproto.IntegerOrString{
Integer: int32Ptr(2304),
},
},
shouldSkip: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// Test the nil check logic that prevents the panic
// This mimics the check in ProvideCodeActions at line 67
shouldSkip := tc.diagnostic.Code == nil || tc.diagnostic.Code.Integer == nil

if shouldSkip != tc.shouldSkip {
t.Errorf("Expected shouldSkip=%v, got %v", tc.shouldSkip, shouldSkip)
}

// Verify we can safely access Code.Integer when shouldSkip is false
if !shouldSkip {
// This should not panic
errorCode := *tc.diagnostic.Code.Integer
if errorCode != 2304 {
t.Errorf("Expected error code 2304, got %d", errorCode)
}
}
})
}
}

func stringPtr(s string) *string {
return &s
}

func int32Ptr(i int32) *int32 {
return &i
}