Repository: go-delve/delve
Pull request #2351 (2021-02-21 10:14:33)

gobuild: add binary removal delay on Windows to avoid access denied


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/installation/README.md b/Documentation/installation/README.md
index 8daa6434..44f52b43 100644
--- a/Documentation/installation/README.md
+++ b/Documentation/installation/README.md
@@ -1,10 +1,50 @@
 # Installation
 
-Directions for installing Delve on all supported platforms is provided here.
+Clone the git repository and build:
 
-Please note you *must* have **Go 1.10** or higher installed in order to compile Delve.
+```
+$ git clone https://github.com/go-delve/delve
+$ cd delve
+$ go install github.com/go-delve/delve/cmd/dlv
+```
 
-- [OSX](osx/install.md)
-- [Linux](linux/install.md)
-- [Windows](windows/install.md)
-- [FreeBSD](freebsd/install.md)
\ No newline at end of file
+On Go version 1.16 or later, this command will also work:
+
+```
+$ go install github.com/go-delve/delve/cmd/dlv@latest
+```
+
+See `go help install` for details on where the `dlv` executable is saved. 
+
+If during the install step you receive an error similar to this:
+
+```
+found packages native (proc.go) and your_operating_system_and_architecture_combination_is_not_supported_by_delve (support_sentinel.go) in /home/pi/go/src/github.com/go-delve/delve/pkg/proc/native
+```
+
+It means that your combination of operating system and CPU architecture is not supported, check the output of `go version`.
+
+## macOS considerations
+
+On macOS make sure you also install the command line developer tools:
+
+```
+$ xcode-select --install
+```
+
+If you didn't enable Developer Mode using Xcode you will be asked to authorize the debugger every time you use it. To enable Developer Mode and only have to authorize once per session use:
+
+```
+sudo /usr/sbin/DevToolsSecurity -enable
+```
+
+## Compiling macOS native backend
+
+You do not need the macOS native backend and it [has known problems](https://github.com/go-delve/delve/issues/1112). If you still want to build it:
+
+1. Run `xcode-select --install`
+2. On macOS 10.14 manually install the legacy include headers by running `/Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg`
+3. Clone the repo into `$GOPATH/src/github.com/go-delve/delve`
+4. Run `make install` in that directory (on some versions of macOS this requires being root, the first time you run it, to install a new certificate)
+
+The makefile will take care of creating and installing a self-signed certificate automatically.
diff --git a/Documentation/installation/freebsd/install.md b/Documentation/installation/freebsd/install.md
index 46c6e4be..ddb60cc4 100644
--- a/Documentation/installation/freebsd/install.md
+++ b/Documentation/installation/freebsd/install.md
@@ -1,20 +1,1 @@
-# Installation on FreeBSD
-
-Please use the following steps to build and install Delve on FreeBSD.
-
-There are two ways to install on FreeBSD. First is the standard `go get` method:
-
-```
-go get github.com/go-delve/delve/cmd/dlv
-```
-
-Note: if you are using Go in modules mode you must execute this command outside of a module directory or Delve will be added to your project as a dependency.
-
-Alternatively make sure $GOPATH is set (e.g. as `~/.go`) and:
-
-```
-$ git clone https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve
-$ cd $GOPATH/src/github.com/go-delve/delve
-$ gmake install
-```
-
+See [general install instructions](../README.md).
\ No newline at end of file
diff --git a/Documentation/installation/linux/install.md b/Documentation/installation/linux/install.md
index ffa27325..0efc16ed 100644
--- a/Documentation/installation/linux/install.md
+++ b/Documentation/installation/linux/install.md
@@ -1,19 +1,1 @@
-# Installation on Linux
-
-Please use the following steps to build and install Delve on Linux.
-
-There are two ways to install on Linux. First is the standard `go get` method:
-
-```
-go get github.com/go-delve/delve/cmd/dlv
-```
-
-Note: if you are using Go in modules mode you must execute this command outside of a module directory or Delve will be added to your project as a dependency.
-
-Alternatively make sure $GOPATH is set (e.g. as `~/.go`) and:
-
-```
-$ git clone https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve
-$ cd $GOPATH/src/github.com/go-delve/delve
-$ make install
-```
+See [general install instructions](../README.md).
diff --git a/Documentation/installation/osx/install.md b/Documentation/installation/osx/install.md
index f4f3f88e..0efc16ed 100644
--- a/Documentation/installation/osx/install.md
+++ b/Documentation/installation/osx/install.md
@@ -1,34 +1,1 @@
-# Installation on macOS
-
-Ensure you have a proper compilation toolchain.
-
-This should be as simple as:
-
-`xcode-select --install`
-
-Now you can install delve using `go get`:
-
-```
-$ go get github.com/go-delve/delve/cmd/dlv
-```
-
-Note: if you are using Go in modules mode you must execute this command outside of a module directory or Delve will be added to your project as a dependency.
-
-With this method you will not be able to use delve's native backend, *but you don't need it anyway*: the native backend on macOS [has known problems](https://github.com/go-delve/delve/issues/1112) on recent issues of the OS and is not currently maintained.
-
-If you didn't enable Developer Mode using Xcode you will be asked to authorize the debugger every time you use it. To enable Developer Mode and only have to authorize once per session use:
-
-```
-sudo /usr/sbin/DevToolsSecurity -enable
-```
-
-## Compiling the native backend
-
-Only do this if you have a valid reason to use the native backend.
-
-1. Run `xcode-select --install`
-2. On macOS 10.14 manually install the legacy include headers by running `/Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg`
-3. Clone the repo into `$GOPATH/src/github.com/go-delve/delve`
-4. Run `make install` in that directory (on some versions of macOS this requires being root, the first time you run it, to install a new certificate)
-
-The makefile will take care of creating and installing a self-signed certificate automatically.
+See [general install instructions](../README.md).
diff --git a/Documentation/installation/windows/install.md b/Documentation/installation/windows/install.md
index 30baabce..ddb60cc4 100644
--- a/Documentation/installation/windows/install.md
+++ b/Documentation/installation/windows/install.md
@@ -1,11 +1,1 @@
-# Installation on Windows
-
-Please use the standard `go get` command to build and install Delve on Windows.
-
-```
-go get github.com/go-delve/delve/cmd/dlv
-```
-
-Note: if you are using Go in modules mode you must execute this command outside of a module directory or Delve will be added to your project as a dependency.
-
-Also, if not already set, you have to add the %GOPATH%\bin directory to your PATH variable.
+See [general install instructions](../README.md).
\ No newline at end of file
diff --git a/pkg/gobuild/gobuild.go b/pkg/gobuild/gobuild.go
index fde64f80..b441b436 100644
--- a/pkg/gobuild/gobuild.go
+++ b/pkg/gobuild/gobuild.go
@@ -6,6 +6,8 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"runtime"
+	"time"
 
 	"github.com/go-delve/delve/pkg/config"
 	"github.com/go-delve/delve/pkg/goversion"
@@ -15,6 +17,12 @@ import (
 // This can be used to remove the temporary binary generated for the session.
 func Remove(path string) {
 	err := os.Remove(path)
+	// Open files can be removed on Unix, but not on Windows, where there also appears
+	// to be a delay in releasing the binary when the process exits. So we try again.
+	if err != nil && runtime.GOOS == "windows" {
+		time.Sleep(10 * time.Microsecond)
+		err = os.Remove(path)
+	}
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "could not remove %v: %v\n", path, err)
 	}
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 28917137..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)


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/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go
index 8a9874a6..3d52a509 100644
--- a/cmd/dlv/dlv_test.go
+++ b/cmd/dlv/dlv_test.go
@@ -170,13 +170,11 @@ func testOutput(t *testing.T, dlvbin, output string, delveCmds []string) (stdout
 
 	_, err = os.Stat(debugbin)
 	if err == nil {
-		if strings.ToLower(os.Getenv("TRAVIS")) == "true" && runtime.GOOS == "windows" {
-			// Sometimes delve on Travis on Windows can't remove the built binary before
-			// exiting and gets an "Access is denied" error when trying.
-			// Just ignore it.
-			// See: https://travis-ci.com/go-delve/delve/jobs/296325131
-			return
-		}
+		// Sometimes delve on Windows can't remove the built binary before
+		// exiting and gets an "Access is denied" error when trying.
+		// This used to make this test flaky. See: https://travis-ci.com/go-delve/delve/jobs/296325131)
+		// But we have now added a delay to gobuild.Remove. If this flakes again,
+		// adjust it. Leaving temporary files behind can be annoying to users.
 		t.Errorf("running %q: file %v was not deleted\nstdout is %q, stderr is %q", delveCmds, debugbin, stdout, stderr)
 		return
 	}
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,