Skip to content

Commit 2884375

Browse files
committed
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 <[email protected]> Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Commit-Queue: Alan Donovan <[email protected]>
1 parent b0f680c commit 2884375

File tree

2 files changed

+45
-8
lines changed

2 files changed

+45
-8
lines changed

gopls/internal/golang/comment.go

+32-8
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func parseDocLink(pkg *cache.Package, pgf *parsego.File, pos token.Pos) (types.O
111111

112112
for _, idx := range docLinkRegex.FindAllStringSubmatchIndex(text, -1) {
113113
// The [idx[2], idx[3]) identifies the first submatch,
114-
// which is the reference name in the doc link.
114+
// which is the reference name in the doc link (sans '*').
115115
// e.g. The "[fmt.Println]" reference name is "fmt.Println".
116116
if !(idx[2] <= lineOffset && lineOffset < idx[3]) {
117117
continue
@@ -126,7 +126,7 @@ func parseDocLink(pkg *cache.Package, pgf *parsego.File, pos token.Pos) (types.O
126126
name = name[:i]
127127
i = strings.LastIndexByte(name, '.')
128128
}
129-
obj := lookupObjectByName(pkg, pgf, name)
129+
obj := lookupDocLinkSymbol(pkg, pgf, name)
130130
if obj == nil {
131131
return nil, protocol.Range{}, errNoCommentReference
132132
}
@@ -141,19 +141,42 @@ func parseDocLink(pkg *cache.Package, pgf *parsego.File, pos token.Pos) (types.O
141141
return nil, protocol.Range{}, errNoCommentReference
142142
}
143143

144-
func lookupObjectByName(pkg *cache.Package, pgf *parsego.File, name string) types.Object {
144+
// lookupDocLinkSymbol returns the symbol denoted by a doc link such
145+
// as "fmt.Println" or "bytes.Buffer.Write" in the specified file.
146+
func lookupDocLinkSymbol(pkg *cache.Package, pgf *parsego.File, name string) types.Object {
145147
scope := pkg.Types().Scope()
148+
149+
prefix, suffix, _ := strings.Cut(name, ".")
150+
151+
// Try treating the prefix as a package name,
152+
// allowing for non-renaming and renaming imports.
146153
fileScope := pkg.TypesInfo().Scopes[pgf.File]
147-
pkgName, suffix, _ := strings.Cut(name, ".")
148-
obj, ok := fileScope.Lookup(pkgName).(*types.PkgName)
149-
if ok {
150-
scope = obj.Imported().Scope()
154+
pkgname, ok := fileScope.Lookup(prefix).(*types.PkgName) // ok => prefix is imported name
155+
if !ok {
156+
// Handle renaming import, e.g.
157+
// [path.Join] after import pathpkg "path".
158+
// (Should we look at all files of the package?)
159+
for _, imp := range pgf.File.Imports {
160+
pkgname2 := pkg.TypesInfo().PkgNameOf(imp)
161+
if pkgname2 != nil && pkgname2.Imported().Name() == prefix {
162+
pkgname = pkgname2
163+
break
164+
}
165+
}
166+
}
167+
if pkgname != nil {
168+
scope = pkgname.Imported().Scope()
151169
if suffix == "" {
152-
return obj
170+
return pkgname // not really a valid doc link
153171
}
154172
name = suffix
155173
}
156174

175+
// TODO(adonovan): try searching the forward closure for packages
176+
// that define the symbol but are not directly imported;
177+
// see https://github.com/golang/go/issues/61677
178+
179+
// Type.Method?
157180
recv, method, ok := strings.Cut(name, ".")
158181
if ok {
159182
obj, ok := scope.Lookup(recv).(*types.TypeName)
@@ -173,5 +196,6 @@ func lookupObjectByName(pkg *cache.Package, pgf *parsego.File, name string) type
173196
return nil
174197
}
175198

199+
// package-level symbol
176200
return scope.Lookup(name)
177201
}

gopls/internal/test/marker/testdata/definition/comment.txt

+13
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@ module mod.com
55

66
go 1.19
77

8+
-- path/path.go --
9+
package path
10+
11+
func Join() //@loc(Join, "Join")
12+
813
-- a.go --
914
package p
1015

1116
import "strconv" //@loc(strconv, `"strconv"`)
17+
import pathpkg "mod.com/path"
1218

1319
const NumberBase = 10 //@loc(NumberBase, "NumberBase")
1420

@@ -19,3 +25,10 @@ func Conv(s string) int { //@loc(Conv, "Conv")
1925
i, _ := strconv.ParseInt(s, NumberBase, 64)
2026
return int(i)
2127
}
28+
29+
// The declared and imported names of the package both work:
30+
// [path.Join] //@ def("Join", Join)
31+
// [pathpkg.Join] //@ def("Join", Join)
32+
func _() {
33+
pathpkg.Join()
34+
}

0 commit comments

Comments
 (0)