gopls/internal/golang: Definitions: support renaming imports in doc links
This CL adds support for jumping to the definition of a doc link when
the import is renamed. Before, the doc link had to use the local
(renamed) name, which is unnatural; now, it can use either the local
name or the package's declared name.
+ test
Updates golang/go#61677
Change-Id: Ibbe18ab1527800c41900d42781677ad892b55cd4
Reviewed-on: https://go-review.googlesource.com/c/tools/+/612045
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Commit-Queue: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/golang/comment.go b/gopls/internal/golang/comment.go
index dc8c1c8..3a0d815 100644
--- a/gopls/internal/golang/comment.go
+++ b/gopls/internal/golang/comment.go
@@ -111,7 +111,7 @@
for _, idx := range docLinkRegex.FindAllStringSubmatchIndex(text, -1) {
// The [idx[2], idx[3]) identifies the first submatch,
- // which is the reference name in the doc link.
+ // which is the reference name in the doc link (sans '*').
// e.g. The "[fmt.Println]" reference name is "fmt.Println".
if !(idx[2] <= lineOffset && lineOffset < idx[3]) {
continue
@@ -126,7 +126,7 @@
name = name[:i]
i = strings.LastIndexByte(name, '.')
}
- obj := lookupObjectByName(pkg, pgf, name)
+ obj := lookupDocLinkSymbol(pkg, pgf, name)
if obj == nil {
return nil, protocol.Range{}, errNoCommentReference
}
@@ -141,19 +141,42 @@
return nil, protocol.Range{}, errNoCommentReference
}
-func lookupObjectByName(pkg *cache.Package, pgf *parsego.File, name string) types.Object {
+// lookupDocLinkSymbol returns the symbol denoted by a doc link such
+// as "fmt.Println" or "bytes.Buffer.Write" in the specified file.
+func lookupDocLinkSymbol(pkg *cache.Package, pgf *parsego.File, name string) types.Object {
scope := pkg.Types().Scope()
+
+ prefix, suffix, _ := strings.Cut(name, ".")
+
+ // Try treating the prefix as a package name,
+ // allowing for non-renaming and renaming imports.
fileScope := pkg.TypesInfo().Scopes[pgf.File]
- pkgName, suffix, _ := strings.Cut(name, ".")
- obj, ok := fileScope.Lookup(pkgName).(*types.PkgName)
- if ok {
- scope = obj.Imported().Scope()
+ pkgname, ok := fileScope.Lookup(prefix).(*types.PkgName) // ok => prefix is imported name
+ if !ok {
+ // Handle renaming import, e.g.
+ // [path.Join] after import pathpkg "path".
+ // (Should we look at all files of the package?)
+ for _, imp := range pgf.File.Imports {
+ pkgname2 := pkg.TypesInfo().PkgNameOf(imp)
+ if pkgname2 != nil && pkgname2.Imported().Name() == prefix {
+ pkgname = pkgname2
+ break
+ }
+ }
+ }
+ if pkgname != nil {
+ scope = pkgname.Imported().Scope()
if suffix == "" {
- return obj
+ return pkgname // not really a valid doc link
}
name = suffix
}
+ // TODO(adonovan): try searching the forward closure for packages
+ // that define the symbol but are not directly imported;
+ // see https://github.com/golang/go/issues/61677
+
+ // Type.Method?
recv, method, ok := strings.Cut(name, ".")
if ok {
obj, ok := scope.Lookup(recv).(*types.TypeName)
@@ -173,5 +196,6 @@
return nil
}
+ // package-level symbol
return scope.Lookup(name)
}
diff --git a/gopls/internal/test/marker/testdata/definition/comment.txt b/gopls/internal/test/marker/testdata/definition/comment.txt
index ac253b2..39c8607 100644
--- a/gopls/internal/test/marker/testdata/definition/comment.txt
+++ b/gopls/internal/test/marker/testdata/definition/comment.txt
@@ -5,10 +5,16 @@
go 1.19
+-- path/path.go --
+package path
+
+func Join() //@loc(Join, "Join")
+
-- a.go --
package p
import "strconv" //@loc(strconv, `"strconv"`)
+import pathpkg "mod.com/path"
const NumberBase = 10 //@loc(NumberBase, "NumberBase")
@@ -19,3 +25,10 @@
i, _ := strconv.ParseInt(s, NumberBase, 64)
return int(i)
}
+
+// The declared and imported names of the package both work:
+// [path.Join] //@ def("Join", Join)
+// [pathpkg.Join] //@ def("Join", Join)
+func _() {
+ pathpkg.Join()
+}