Repository: go-delve/delve
Pull request #2347 (2021-02-18 07:28:31)

service/dap: include optional breakpoint info in setBreakpoints responses
Updates https://github.com/golang/vscode-go/issues/1212

Following the same response pattern as the original vscode-go adapter seems to no longer result in the same vscode UI behavior - see BREAKPOINTS and margin marker. Adding optional information fixes that.

Before
![image](https://user-images.githubusercontent.com/51177946/108320272-6be2ba80-7177-11eb-9221-11e5b23f28f9.png)
After
![image](https://user-images.githubusercontent.com/51177946/108320486-b6643700-7177-11eb-9a9d-e0c42c144adc.png)



Patch:
diff --git a/.teamcity/settings.kts b/.teamcity/settings.kts
index ec8b3cb5..8ea5cf63 100644
--- a/.teamcity/settings.kts
+++ b/.teamcity/settings.kts
@@ -34,15 +34,13 @@ To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
 version = "2020.2"
 
 val targets = arrayOf(
-	"linux/amd64/1.13",
 	"linux/amd64/1.14",
         "linux/amd64/1.15",
         "linux/amd64/1.16",
         "linux/amd64/tip",
 
-        "linux/386/1.15",
+        "linux/386/1.16",
 
-        "windows/amd64/1.15",
         "windows/amd64/1.16",
         "windows/amd64/tip"
 )
diff --git a/Documentation/api/json-rpc/README.md b/Documentation/api/json-rpc/README.md
index c690c480..4c1fb2ea 100644
--- a/Documentation/api/json-rpc/README.md
+++ b/Documentation/api/json-rpc/README.md
@@ -1,11 +1,9 @@
 # JSON-RPC interface
 
-Delve exposes a [JSON-RPC](http://json-rpc.org/) API interface. 
+Delve exposes a [JSON-RPC](https://www.jsonrpc.org/specification_v1) API interface. 
 
 Note that this JSON-RPC interface is served over a streaming socket, *not* over HTTP.
 
-Here is an (incomplete) [list of language implementations](http://json-rpc.org/wiki/implementations).
-
 # API versions
 
 Delve currently supports two versions of its API. By default a headless instance of `dlv` will serve APIv1 for backward compatibility with old clients, however new clients should use APIv2 as new features will only be made available through version 2. To select APIv2 use `--api-version=2` command line argument. 
diff --git a/pkg/goversion/compat.go b/pkg/goversion/compat.go
index 270effd2..f800b38c 100644
--- a/pkg/goversion/compat.go
+++ b/pkg/goversion/compat.go
@@ -6,7 +6,7 @@ import (
 
 var (
 	MinSupportedVersionOfGoMajor = 1
-	MinSupportedVersionOfGoMinor = 13
+	MinSupportedVersionOfGoMinor = 14
 	MaxSupportedVersionOfGoMajor = 1
 	MaxSupportedVersionOfGoMinor = 16
 	goTooOldErr                  = fmt.Errorf("Version of Go is too old for this version of Delve (minimum supported version %d.%d, suppress this error with --check-go-version=false)", MinSupportedVersionOfGoMajor, MinSupportedVersionOfGoMinor)
diff --git a/service/dap/server.go b/service/dap/server.go
index 7206d133..7069a87f 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -599,7 +599,9 @@ func (s *Server) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
 			response.Body.Breakpoints[i].Line = want.Line
 			response.Body.Breakpoints[i].Message = err.Error()
 		} else {
+			response.Body.Breakpoints[i].Id = got.ID
 			response.Body.Breakpoints[i].Line = got.Line
+			response.Body.Breakpoints[i].Source = dap.Source{Name: request.Arguments.Source.Name, Path: request.Arguments.Source.Path}
 		}
 	}
 	s.send(response)
@@ -1410,7 +1412,7 @@ func newEvent(event string) *dap.Event {
 }
 
 const BetterBadAccessError = `invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation]
-Unable to propogate EXC_BAD_ACCESS signal to target process and panic (see https://github.com/go-delve/delve/issues/852)`
+Unable to propagate EXC_BAD_ACCESS signal to target process and panic (see https://github.com/go-delve/delve/issues/852)`
 
 func (s *Server) resetHandlesForStop() {
 	s.stackFrameHandles.reset()


Test patch:
diff --git a/_fixtures/testvariables2.go b/_fixtures/testvariables2.go
index 7efb06aa..4fcee1cf 100644
--- a/_fixtures/testvariables2.go
+++ b/_fixtures/testvariables2.go
@@ -144,6 +144,7 @@ func main() {
 	f1 := 3.0
 	i3 := 3
 	p1 := &i1
+	pp1 := &p1
 	s1 := []string{"one", "two", "three", "four", "five"}
 	s3 := make([]int, 0, 6)
 	a0 := [0]int{}
@@ -357,5 +358,5 @@ func main() {
 	}
 
 	runtime.Breakpoint()
-	fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, upnil, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, errtypednil, emptyslice, emptymap, byteslice, runeslice, bytearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5)
+	fmt.Println(i1, i2, i3, p1, pp1, amb1, s1, s3, a0, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, m4, upnil, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, ni64, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, errtypednil, emptyslice, emptymap, byteslice, runeslice, bytearray, runearray, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4, iface2map, issue1578, ll, unread, w2, w3, w4, w5)
 }
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index aecf5520..92c8b05a 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -419,8 +419,8 @@ func TestPreSetBreakpoint(t *testing.T) {
 			t.Errorf("got %#v, want len(Breakpoints)=1", sResp)
 		}
 		bkpt0 := sResp.Body.Breakpoints[0]
-		if !bkpt0.Verified || bkpt0.Line != 8 {
-			t.Errorf("got breakpoints[0] = %#v, want Verified=true, Line=8", bkpt0)
+		if !bkpt0.Verified || bkpt0.Line != 8 || bkpt0.Id != 1 || bkpt0.Source.Name != filepath.Base(fixture.Source) || bkpt0.Source.Path != fixture.Source {
+			t.Errorf("got breakpoints[0] = %#v, want Verified=true, Line=8, Id=1, Path=%q", bkpt0, fixture.Source)
 		}
 
 		client.SetExceptionBreakpointsRequest()
@@ -1139,6 +1139,20 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 						expectVarRegex(t, err1, 0, "data", `err1\.\(data\)`, `<\*main\.astruct>\(0x[0-9a-f]+\)`, hasChildren)
 						validateEvaluateName(t, client, err1, 0)
 					}
+					ref = expectVarRegex(t, locals, -1, "ptrinf", "ptrinf", `<\*interface {}>\(0x[0-9a-f]+\)`, hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						ptrinf_val := client.ExpectVariablesResponse(t)
+						expectChildren(t, ptrinf_val, "*ptrinf", 1)
+						ref = expectVarExact(t, ptrinf_val, 0, "", "(*ptrinf)", "<interface {}(**interface {})>", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							ptrinf_val_data := client.ExpectVariablesResponse(t)
+							expectChildren(t, ptrinf_val_data, "(*ptrinf).data", 1)
+							expectVarRegex(t, ptrinf_val_data, 0, "data", `\(\*ptrinf\)\.\(data\)`, `<\*\*interface {}>\(0x[0-9a-f]+\)`, hasChildren)
+							validateEvaluateName(t, client, ptrinf_val_data, 0)
+						}
+					}
 					// reflect.Kind == Map
 					expectVarExact(t, locals, -1, "mnil", "mnil", "nil <map[string]main.astruct>", noChildren)
 					// key - scalar, value - compound
@@ -1217,7 +1231,21 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 						}
 					}
 					expectVarExact(t, locals, -1, "emptymap", "emptymap", "<map[string]string> (length: 0)", noChildren)
-					// reflect.Kind == Ptr - see testvariables
+					// reflect.Kind == Ptr
+					ref = expectVarRegex(t, locals, -1, "pp1", "pp1", `<\*\*int>\(0x[0-9a-f]+\)`, hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						pp1val := client.ExpectVariablesResponse(t)
+						expectChildren(t, pp1val, "*pp1", 1)
+						ref = expectVarRegex(t, pp1val, 0, "", `\(\*pp1\)`, `<\*int>\(0x[0-9a-f]+\)`, hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							pp1valval := client.ExpectVariablesResponse(t)
+							expectChildren(t, pp1valval, "*(*pp1)", 1)
+							expectVarExact(t, pp1valval, 0, "", "(*(*pp1))", "1", noChildren)
+							validateEvaluateName(t, client, pp1valval, 0)
+						}
+					}
 					// reflect.Kind == Slice
 					ref = expectVarExact(t, locals, -1, "zsslice", "zsslice", "<[]struct {}> (length: 3, cap: 3)", hasChildren)
 					if ref > 0 {
@@ -1255,6 +1283,63 @@ func TestScopesAndVariablesRequests2(t *testing.T) {
 						m1 := client.ExpectVariablesResponse(t)
 						expectChildren(t, m1, "m1", 64) // TODO(polina): should be 66.
 					}
+
+					ref = expectVarExact(t, locals, -1, "c1", "c1", "<main.cstruct>", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						c1 := client.ExpectVariablesResponse(t)
+						expectChildren(t, c1, "c1", 2)
+						ref = expectVarExact(t, c1, 1, "sa", "c1.sa", "<[]*main.astruct> (length: 3, cap: 3)", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							c1sa := client.ExpectVariablesResponse(t)
+							expectChildren(t, c1sa, "c1.sa", 3)
+							ref = expectVarRegex(t, c1sa, 0, `\[0\]`, `c1\.sa\[0\]`, `<\*main\.astruct>\(0x[0-9a-f]+\)`, hasChildren)
+							if ref > 0 {
+								client.VariablesRequest(ref)
+								c1sa0 := client.ExpectVariablesResponse(t)
+								expectChildren(t, c1sa0, "c1.sa[0]", 1)
+								// TODO(polina): there should be a child here once we support auto loading
+								expectVarExact(t, c1sa0, 0, "", "(*c1.sa[0])", "<main.astruct>", noChildren)
+							}
+						}
+					}
+
+					ref = expectVarExact(t, locals, -1, "aas", "aas", "<[]main.a> (length: 1, cap: 1)", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						aas := client.ExpectVariablesResponse(t)
+						expectChildren(t, aas, "aas", 1)
+						ref = expectVarExact(t, aas, 0, "[0]", "aas[0]", "<main.a>", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							aas0 := client.ExpectVariablesResponse(t)
+							expectChildren(t, aas0, "aas[0]", 1)
+							ref = expectVarExact(t, aas0, 0, "aas", "aas[0].aas", "<[]main.a> (length: 1, cap: 1)", hasChildren)
+							if ref > 0 {
+								client.VariablesRequest(ref)
+								aas0aas := client.ExpectVariablesResponse(t)
+								expectChildren(t, aas0aas, "aas[0].aas", 1)
+								// TODO(polina): there should be a child here once we support auto loading - test for "aas[0].aas[0].aas"
+								expectVarExact(t, aas0aas, 0, "[0]", "aas[0].aas[0]", "<main.a>", noChildren)
+							}
+						}
+					}
+
+					ref = expectVarExact(t, locals, -1, "tm", "tm", "<main.truncatedMap>", hasChildren)
+					if ref > 0 {
+						client.VariablesRequest(ref)
+						tm := client.ExpectVariablesResponse(t)
+						expectChildren(t, tm, "tm", 1)
+						ref = expectVarExact(t, tm, 0, "v", "tm.v", "<[]map[string]main.astruct> (length: 1, cap: 1)", hasChildren)
+						if ref > 0 {
+							client.VariablesRequest(ref)
+							tm_v := client.ExpectVariablesResponse(t)
+							expectChildren(t, tm_v, "tm.v", 1)
+							// TODO(polina): there should be children here once we support auto loading - test for "tm.v[0]["gutters"]"
+							expectVarExact(t, tm_v, 0, "[0]", "tm.v[0]", "<map[string]main.astruct> (length: 66)", noChildren)
+						}
+					}
 				},
 				disconnect: true,
 			}})
@@ -1371,6 +1456,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					type Breakpoint struct {
 						line      int
+						path      string
 						verified  bool
 						msgPrefix string
 					}
@@ -1382,7 +1468,7 @@ func TestSetBreakpoint(t *testing.T) {
 							return
 						}
 						for i, bp := range got.Body.Breakpoints {
-							if bp.Line != bps[i].line || bp.Verified != bps[i].verified ||
+							if bp.Line != bps[i].line || bp.Verified != bps[i].verified || bp.Source.Path != bps[i].path ||
 								!strings.HasPrefix(bp.Message, bps[i].msgPrefix) {
 								t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i])
 							}
@@ -1391,11 +1477,11 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Set two breakpoints at the next two lines in main
 					client.SetBreakpointsRequest(fixture.Source, []int{17, 18})
-					expectSetBreakpointsResponse([]Breakpoint{{17, true, ""}, {18, true, ""}})
+					expectSetBreakpointsResponse([]Breakpoint{{17, fixture.Source, true, ""}, {18, fixture.Source, true, ""}})
 
 					// Clear 17, reset 18
 					client.SetBreakpointsRequest(fixture.Source, []int{18})
-					expectSetBreakpointsResponse([]Breakpoint{{18, true, ""}})
+					expectSetBreakpointsResponse([]Breakpoint{{18, fixture.Source, true, ""}})
 
 					// Skip 17, continue to 18
 					client.ContinueRequest(1)
@@ -1405,7 +1491,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Set another breakpoint inside the loop in loop(), twice to trigger error
 					client.SetBreakpointsRequest(fixture.Source, []int{8, 8})
-					expectSetBreakpointsResponse([]Breakpoint{{8, true, ""}, {8, false, "Breakpoint exists"}})
+					expectSetBreakpointsResponse([]Breakpoint{{8, fixture.Source, true, ""}, {8, "", false, "Breakpoint exists"}})
 
 					// Continue into the loop
 					client.ContinueRequest(1)
@@ -1418,7 +1504,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Edit the breakpoint to add a condition
 					client.SetConditionalBreakpointsRequest(fixture.Source, []int{8}, map[int]string{8: "i == 3"})
-					expectSetBreakpointsResponse([]Breakpoint{{8, true, ""}})
+					expectSetBreakpointsResponse([]Breakpoint{{8, fixture.Source, true, ""}})
 
 					// Continue until condition is hit
 					client.ContinueRequest(1)
@@ -1431,7 +1517,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Edit the breakpoint to remove a condition
 					client.SetConditionalBreakpointsRequest(fixture.Source, []int{8}, map[int]string{8: ""})
-					expectSetBreakpointsResponse([]Breakpoint{{8, true, ""}})
+					expectSetBreakpointsResponse([]Breakpoint{{8, fixture.Source, true, ""}})
 
 					// Continue for one more loop iteration
 					client.ContinueRequest(1)
@@ -1444,7 +1530,7 @@ func TestSetBreakpoint(t *testing.T) {
 
 					// Set at a line without a statement
 					client.SetBreakpointsRequest(fixture.Source, []int{1000})
-					expectSetBreakpointsResponse([]Breakpoint{{1000, false, "could not find statement"}}) // all cleared, none set
+					expectSetBreakpointsResponse([]Breakpoint{{1000, "", false, "could not find statement"}}) // all cleared, none set
 				},
 				// The program has an infinite loop, so we must kill it by disconnecting.
 				disconnect: true,