internal/jsonrpc2: don't set result in response if there is an error

Marshalling an empty raw message produces a null rather than omitting the field
Instead we do not set the field unless there is no error.
Includes a test that reproduced the issue before the fix.

Fixes golang/go#39719

Change-Id: I683b8ea55d3425613fc956993280ff51202f49fc
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239097
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/internal/jsonrpc2/messages.go b/internal/jsonrpc2/messages.go
index ff2bd47..58d285d 100644
--- a/internal/jsonrpc2/messages.go
+++ b/internal/jsonrpc2/messages.go
@@ -146,7 +146,10 @@
 func (msg *Response) isJSONRPC2Message()      {}
 
 func (r *Response) MarshalJSON() ([]byte, error) {
-	msg := &wireResponse{Result: &r.result, Error: toWireError(r.err), ID: &r.id}
+	msg := &wireResponse{Error: toWireError(r.err), ID: &r.id}
+	if msg.Error == nil {
+		msg.Result = &r.result
+	}
 	data, err := json.Marshal(msg)
 	if err != nil {
 		return data, fmt.Errorf("marshaling notification: %w", err)
diff --git a/internal/jsonrpc2/wire_test.go b/internal/jsonrpc2/wire_test.go
index 7cddb65..4d8832c 100644
--- a/internal/jsonrpc2/wire_test.go
+++ b/internal/jsonrpc2/wire_test.go
@@ -81,6 +81,25 @@
 	}
 }
 
+func TestErrorResponse(t *testing.T) {
+	// originally reported in #39719, this checks that result is not present if
+	// it is an error response
+	r, _ := jsonrpc2.NewResponse(jsonrpc2.NewIntID(3), nil, fmt.Errorf("computing fix edits"))
+	data, err := json.Marshal(r)
+	if err != nil {
+		t.Fatal(err)
+	}
+	checkJSON(t, data, []byte(`{
+		"jsonrpc":"2.0",
+		"error":{
+			"code":0,
+			"message":"computing fix edits",
+			"data":null
+		},
+		"id":3
+	}`))
+}
+
 func checkJSON(t *testing.T, got, want []byte) {
 	// compare the compact form, to allow for formatting differences
 	g := &bytes.Buffer{}