Repository: go-delve/delve
Pull request #2496 (2021-05-18 20:48:19)

service/dap: support evaluate for hover context
We can support evaluate for hover context in order to provide more
useful hover info for dap users. This can be particularly useful when
hovering over fields in a struct type.

Updates go-delve/delve#2491
Updates golang/vscode-go#1510


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b08b9ef..0b4e6e73 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,41 @@
 All notable changes to this project will be documented in this file.
 This project adheres to Semantic Versioning.
 
+## [1.6.1] 2021-05-18
+
+### Added
+
+- Dump command: generate core dumps from within Delve (@aarzilli)
+- Toggle command: toggle breakpoints on or off (@alexsaezm)
+- DAP server improvements (@polinasok, @hyangah, @suzmue)
+- Delve now parses and uses the .eh_frame section when available (@aarzilli)
+- Add linespec argument to 'continue' command (@icholy)
+- Add optional format argument to 'print' and 'display' commands (@aarzilli)
+
+### Fixed
+
+- Fixed file reference handling with DWARF5 compilation units (@thanm)
+- Fix embedded field searching (@aarzilli)
+- Fix off by one error reading core files (@aarzilli)
+- Correctly read G address on linux/arm64
+- Avoid double removal of temp built binary (@polinasok)
+- Fix temp binary deletion race in DAP server (@polinasok)
+- Fix shutdown related bugs in RPC server (@aarzilli)
+- Fix crashes induced by RequestManualStop (@aarzilli)
+- Fix handling of DW_OP_piece (@aarzilli)
+- Correctly truncate the result of binary operations on integers (@aarzilli)
+
+### Changed
+
+- Dropped Go 1.13 support (@aarzilli)
+- Improved documentation (@ChrisHines, @aarzilli, @becheran, @hedenface, @andreimatei, @ehershey , @hyangah)
+- Allow examinememory to use an expression (@felixge)
+- Improve gdb server check on newer ARM based macs (@oxisto)
+- CPU registers can now be used in expressions (@aarzilli)
+- DAP: Add type information to variables (@suzmue)
+- DAP: Support setting breakpoints while target is running (@polinasok)
+- DAP: Implement function breakpoints (@suzmue)
+
 ## [1.6.0] 2021-01-28
 
 ### Added
diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go
index 3d097b26..1f0c8a8e 100644
--- a/cmd/dlv/cmds/commands.go
+++ b/cmd/dlv/cmds/commands.go
@@ -452,6 +452,7 @@ func dapCmd(cmd *cobra.Command, args []string) {
 				CheckGoVersion:       checkGoVersion,
 				TTY:                  tty,
 			},
+			CheckLocalConnUser: checkLocalConnUser,
 		})
 		defer server.Stop()
 
diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go
index 87c8559e..b8af4e91 100644
--- a/pkg/proc/bininfo.go
+++ b/pkg/proc/bininfo.go
@@ -670,7 +670,7 @@ type Image struct {
 func (image *Image) registerRuntimeTypeToDIE(entry *dwarf.Entry, ardr *reader.Reader) {
 	if off, ok := entry.Val(godwarf.AttrGoRuntimeType).(uint64); ok {
 		if _, ok := image.runtimeTypeToDIE[off]; !ok {
-			image.runtimeTypeToDIE[off+image.StaticBase] = runtimeTypeDIE{entry.Offset, -1}
+			image.runtimeTypeToDIE[off] = runtimeTypeDIE{entry.Offset, -1}
 		}
 	}
 }
diff --git a/pkg/proc/types.go b/pkg/proc/types.go
index 2a5dc303..d052b61d 100644
--- a/pkg/proc/types.go
+++ b/pkg/proc/types.go
@@ -11,6 +11,7 @@ import (
 
 	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 	"github.com/go-delve/delve/pkg/dwarf/reader"
+	"github.com/go-delve/delve/pkg/goversion"
 )
 
 // The kind field in runtime._type is a reflect.Kind value plus
@@ -149,6 +150,15 @@ func runtimeTypeToDIE(_type *Variable, dataAddr uint64) (typ godwarf.Type, kind
 
 	// go1.7 to go1.10 implementation: convert runtime._type structs to type names
 
+	if goversion.ProducerAfterOrEqual(_type.bi.Producer(), 1, 17) {
+		// Go 1.17 changed the encoding of names in runtime._type breaking the
+		// code below, but the codepath above, using runtimeTypeToDIE should be
+		// enough.
+		// The change happened in commit 287025925f66f90ad9b30aea2e533928026a8376
+		// reviewed in https://go-review.googlesource.com/c/go/+/318249
+		return nil, 0, fmt.Errorf("could not resolve interface type")
+	}
+
 	typename, kind, err := nameOfRuntimeType(mds, _type)
 	if err != nil {
 		return nil, 0, fmt.Errorf("invalid interface type: %v", err)
diff --git a/pkg/version/version.go b/pkg/version/version.go
index 8cc97519..114218c2 100644
--- a/pkg/version/version.go
+++ b/pkg/version/version.go
@@ -14,7 +14,7 @@ type Version struct {
 var (
 	// DelveVersion is the current version of Delve.
 	DelveVersion = Version{
-		Major: "1", Minor: "6", Patch: "0", Metadata: "",
+		Major: "1", Minor: "6", Patch: "1", Metadata: "",
 		Build: "$Id$",
 	}
 )
diff --git a/service/dap/server.go b/service/dap/server.go
index 40843093..28166c03 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -34,6 +34,7 @@ import (
 	"github.com/go-delve/delve/service"
 	"github.com/go-delve/delve/service/api"
 	"github.com/go-delve/delve/service/debugger"
+	"github.com/go-delve/delve/service/internal/sameuser"
 	"github.com/google/go-dap"
 	"github.com/sirupsen/logrus"
 )
@@ -320,6 +321,13 @@ func (s *Server) Run() {
 			}
 			return
 		}
+		if s.config.CheckLocalConnUser {
+			if !sameuser.CanAccept(s.listener.Addr(), conn.RemoteAddr()) {
+				s.log.Error("Error accepting client connection: Only connections from the same user that started this instance of Delve are allowed to connect. See --only-same-user.")
+				s.triggerServerStop()
+				return
+			}
+		}
 		s.mu.Lock()
 		s.conn = conn // closed in Stop()
 		s.mu.Unlock()
@@ -669,6 +677,7 @@ func (s *Server) onInitializeRequest(request *dap.InitializeRequest) {
 	response.Body.SupportTerminateDebuggee = true
 	response.Body.SupportsFunctionBreakpoints = true
 	response.Body.SupportsExceptionInfoRequest = true
+	response.Body.SupportsEvaluateForHovers = true
 	// TODO(polina): support this to match vscode-go functionality
 	response.Body.SupportsSetVariable = false
 	// TODO(polina): support these requests in addition to vscode-go feature parity
@@ -1747,7 +1756,7 @@ func (s *Server) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr s
 // variables, so consider also adding the following:
 // -- print {expression} - return the result as a string like from dlv cli
 func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) {
-	showErrorToUser := request.Arguments.Context != "watch" && request.Arguments.Context != "repl"
+	showErrorToUser := request.Arguments.Context != "watch" && request.Arguments.Context != "repl" && request.Arguments.Context != "hover"
 	if s.debugger == nil {
 		s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", "debugger is nil", showErrorToUser)
 		return
diff --git a/service/internal/sameuser/doc.go b/service/internal/sameuser/doc.go
new file mode 100644
index 00000000..b6fc8fd1
--- /dev/null
+++ b/service/internal/sameuser/doc.go
@@ -0,0 +1,3 @@
+// Package sameuser provides utilities for checking users of a local connection.
+// Only works in Linux.
+package sameuser
diff --git a/service/internal/sameuser/sameuser.go b/service/internal/sameuser/sameuser.go
new file mode 100644
index 00000000..8ca5ef8b
--- /dev/null
+++ b/service/internal/sameuser/sameuser.go
@@ -0,0 +1,9 @@
+//+build !linux
+
+package sameuser
+
+import "net"
+
+func CanAccept(_, _ net.Addr) bool {
+	return true
+}
diff --git a/service/rpccommon/sameuser_linux.go b/service/internal/sameuser/sameuser_linux.go
similarity index 98%
rename from service/rpccommon/sameuser_linux.go
rename to service/internal/sameuser/sameuser_linux.go
index 4321d192..28e4755c 100644
--- a/service/rpccommon/sameuser_linux.go
+++ b/service/internal/sameuser/sameuser_linux.go
@@ -1,6 +1,6 @@
 //+build linux
 
-package rpccommon
+package sameuser
 
 import (
 	"bytes"
@@ -96,7 +96,7 @@ func sameUserForRemoteAddr(remoteAddr *net.TCPAddr) (bool, error) {
 	return sameUserForRemoteAddr4(remoteAddr)
 }
 
-func canAccept(listenAddr, remoteAddr net.Addr) bool {
+func CanAccept(listenAddr, remoteAddr net.Addr) bool {
 	laddr, ok := listenAddr.(*net.TCPAddr)
 	if !ok || !laddr.IP.IsLoopback() {
 		return true
diff --git a/service/rpccommon/sameuser.go b/service/rpccommon/sameuser.go
deleted file mode 100644
index b2d6e1cc..00000000
--- a/service/rpccommon/sameuser.go
+++ /dev/null
@@ -1,9 +0,0 @@
-//+build !linux
-
-package rpccommon
-
-import "net"
-
-func canAccept(_, _ net.Addr) bool {
-	return true
-}
diff --git a/service/rpccommon/server.go b/service/rpccommon/server.go
index affdd114..a9d8f777 100644
--- a/service/rpccommon/server.go
+++ b/service/rpccommon/server.go
@@ -20,6 +20,7 @@ import (
 	"github.com/go-delve/delve/service"
 	"github.com/go-delve/delve/service/api"
 	"github.com/go-delve/delve/service/debugger"
+	"github.com/go-delve/delve/service/internal/sameuser"
 	"github.com/go-delve/delve/service/rpc1"
 	"github.com/go-delve/delve/service/rpc2"
 	"github.com/sirupsen/logrus"
@@ -145,7 +146,7 @@ func (s *ServerImpl) Run() error {
 			}
 
 			if s.config.CheckLocalConnUser {
-				if !canAccept(s.listener.Addr(), c.RemoteAddr()) {
+				if !sameuser.CanAccept(s.listener.Addr(), c.RemoteAddr()) {
 					c.Close()
 					continue
 				}


Test patch:
diff --git a/service/dap/daptest/client.go b/service/dap/daptest/client.go
index fc3d7dcc..180b6161 100644
--- a/service/dap/daptest/client.go
+++ b/service/dap/daptest/client.go
@@ -102,6 +102,7 @@ func (c *Client) ExpectInitializeResponseAndCapabilities(t *testing.T) *dap.Init
 		SupportTerminateDebuggee:         true,
 		SupportsExceptionInfoRequest:     true,
 		SupportsFunctionBreakpoints:      true,
+		SupportsEvaluateForHovers:        true,
 	}
 	if !reflect.DeepEqual(initResp.Body, wantCapabilities) {
 		t.Errorf("capabilities in initializeResponse: got %+v, want %v", pretty(initResp.Body), pretty(wantCapabilities))
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index dc66c2c8..e41769ce 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -2560,6 +2560,16 @@ func TestEvaluateRequest(t *testing.T) {
 					if erres.Body.Error.Format != "Unable to evaluate expression: could not find symbol value for a1" {
 						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
 					}
+					client.EvaluateRequest("a1", 1002, "repl")
+					erres = client.ExpectInvisibleErrorResponse(t)
+					if erres.Body.Error.Format != "Unable to evaluate expression: could not find symbol value for a1" {
+						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
+					}
+					client.EvaluateRequest("a1", 1002, "hover")
+					erres = client.ExpectInvisibleErrorResponse(t)
+					if erres.Body.Error.Format != "Unable to evaluate expression: could not find symbol value for a1" {
+						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
+					}
 				},
 				disconnect: false,
 			}})
diff --git a/service/rpccommon/sameuser_linux_test.go b/service/internal/sameuser/sameuser_linux_test.go
similarity index 99%
rename from service/rpccommon/sameuser_linux_test.go
rename to service/internal/sameuser/sameuser_linux_test.go
index 9fd2bd4a..8cb185a7 100644
--- a/service/rpccommon/sameuser_linux_test.go
+++ b/service/internal/sameuser/sameuser_linux_test.go
@@ -1,6 +1,6 @@
 //+build linux
 
-package rpccommon
+package sameuser
 
 import (
 	"net"