From e1c3ff1c612074cc9ad97e88222e9c7cd519a8d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:10:08 +0000 Subject: [PATCH 1/3] Initial plan From 9468e80a08b87964878af7e0967b0056a9ff47e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:23:38 +0000 Subject: [PATCH 2/3] Fix panic when diagnostic code is nil in code actions Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/codeactions.go | 2 +- internal/ls/codeactions_test.go | 91 +++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 internal/ls/codeactions_test.go diff --git a/internal/ls/codeactions.go b/internal/ls/codeactions.go index 4cf6ed27dc..a85a9d32ba 100644 --- a/internal/ls/codeactions.go +++ b/internal/ls/codeactions.go @@ -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 } diff --git a/internal/ls/codeactions_test.go b/internal/ls/codeactions_test.go new file mode 100644 index 0000000000..911d732a6c --- /dev/null +++ b/internal/ls/codeactions_test.go @@ -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 +} From b91a4f0c9e57b53a991c18111bca41c7ba6a33e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 14 Nov 2025 22:21:58 +0000 Subject: [PATCH 3/3] Remove codeactions_test.go as requested Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- internal/ls/codeactions_test.go | 91 --------------------------------- 1 file changed, 91 deletions(-) delete mode 100644 internal/ls/codeactions_test.go diff --git a/internal/ls/codeactions_test.go b/internal/ls/codeactions_test.go deleted file mode 100644 index 911d732a6c..0000000000 --- a/internal/ls/codeactions_test.go +++ /dev/null @@ -1,91 +0,0 @@ -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 -}