gopls/internal/lsp/source: fix panic in typeDefinition on comparable
comparable is also permitted to have no position.
Updates golang/go#60544
Change-Id: Ic0694796432ab8b3271a60e4f4f649a1657d462b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/499986
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/lsp/source/identifier.go b/gopls/internal/lsp/source/identifier.go
index 15fe13a..57001af 100644
--- a/gopls/internal/lsp/source/identifier.go
+++ b/gopls/internal/lsp/source/identifier.go
@@ -56,7 +56,7 @@
// typeToObject returns the relevant type name for the given type, after
// unwrapping pointers, arrays, slices, channels, and function signatures with
-// a single non-error result.
+// a single non-error result, and ignoring built-in named types.
func typeToObject(typ types.Type) *types.TypeName {
switch typ := typ.(type) {
case *types.Named:
@@ -79,7 +79,7 @@
for i := 0; i < results.Len(); i++ {
obj := typeToObject(results.At(i).Type())
if obj == nil || hasErrorType(obj) {
- // Skip builtins.
+ // Skip builtins. TODO(rfindley): should comparable be handled here as well?
continue
}
if res != nil {
diff --git a/gopls/internal/lsp/source/type_definition.go b/gopls/internal/lsp/source/type_definition.go
index 73fc965..6c26b16 100644
--- a/gopls/internal/lsp/source/type_definition.go
+++ b/gopls/internal/lsp/source/type_definition.go
@@ -9,6 +9,7 @@
"fmt"
"go/token"
+ "golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/internal/event"
)
@@ -35,19 +36,20 @@
return nil, nil
}
- typObj := typeToObject(obj.Type())
- if typObj == nil {
+ tname := typeToObject(obj.Type())
+ if tname == nil {
return nil, fmt.Errorf("no type definition for %s", obj.Name())
}
- // Identifiers with the type "error" are a special case with no position.
- if hasErrorType(typObj) {
- // TODO(rfindley): we can do better here, returning a link to the builtin
- // file.
+ if !tname.Pos().IsValid() {
+ // The only defined types with no position are error and comparable.
+ if tname.Name() != "error" && tname.Name() != "comparable" {
+ bug.Reportf("unexpected type name with no position: %s", tname)
+ }
return nil, nil
}
- loc, err := mapPosition(ctx, pkg.FileSet(), snapshot, typObj.Pos(), typObj.Pos()+token.Pos(len(typObj.Name())))
+ loc, err := mapPosition(ctx, pkg.FileSet(), snapshot, tname.Pos(), tname.Pos()+token.Pos(len(tname.Name())))
if err != nil {
return nil, err
}
diff --git a/gopls/internal/regtest/misc/definition_test.go b/gopls/internal/regtest/misc/definition_test.go
index 9f24ef6..0a36336 100644
--- a/gopls/internal/regtest/misc/definition_test.go
+++ b/gopls/internal/regtest/misc/definition_test.go
@@ -371,6 +371,28 @@
}
}
+func TestGoToTypeDefinition_Issue60544(t *testing.T) {
+ const mod = `
+-- go.mod --
+module mod.com
+
+go 1.19
+-- main.go --
+package main
+
+func F[T comparable]() {}
+`
+
+ Run(t, mod, func(t *testing.T, env *Env) {
+ env.OpenFile("main.go")
+
+ _, err := env.Editor.GoToTypeDefinition(env.Ctx, env.RegexpSearch("main.go", "comparable")) // must not panic
+ if err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+
// Test for golang/go#47825.
func TestImportTestVariant(t *testing.T) {
const mod = `