Skip to content

Commit 4754748

Browse files
aarzillifindleyr
authored andcommitted
godoc: fix addNames for generics
The type of a function receiver can now also be a IndexExpr. Fixes golang/go#50413 Change-Id: I5ac7bee8ea6b594be00d00c7fed2e2a9fe260b10 Reviewed-on: https://go-review.googlesource.com/c/tools/+/373857 Trust: Than McIntosh <[email protected]> Run-TryBot: Alessandro Arzilli <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent d4d4325 commit 4754748

File tree

2 files changed

+127
-4
lines changed

2 files changed

+127
-4
lines changed

godoc/server.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"golang.org/x/tools/godoc/analysis"
3131
"golang.org/x/tools/godoc/util"
3232
"golang.org/x/tools/godoc/vfs"
33+
"golang.org/x/tools/internal/typeparams"
3334
)
3435

3536
// handlerServer is a migration from an old godoc http Handler type.
@@ -462,12 +463,19 @@ func addNames(names map[string]bool, decl ast.Decl) {
462463
case *ast.FuncDecl:
463464
name := d.Name.Name
464465
if d.Recv != nil {
466+
r := d.Recv.List[0].Type
467+
if rr, isstar := r.(*ast.StarExpr); isstar {
468+
r = rr.X
469+
}
470+
465471
var typeName string
466-
switch r := d.Recv.List[0].Type.(type) {
467-
case *ast.StarExpr:
468-
typeName = r.X.(*ast.Ident).Name
472+
switch x := r.(type) {
469473
case *ast.Ident:
470-
typeName = r.Name
474+
typeName = x.Name
475+
case *ast.IndexExpr:
476+
typeName = x.X.(*ast.Ident).Name
477+
case *typeparams.IndexListExpr:
478+
typeName = x.X.(*ast.Ident).Name
471479
}
472480
name = typeName + "_" + name
473481
}

godoc/server_test.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@
55
package godoc
66

77
import (
8+
"go/doc"
89
"net/http"
910
"net/http/httptest"
1011
"net/url"
12+
"sort"
1113
"strings"
1214
"testing"
1315
"text/template"
1416

1517
"golang.org/x/tools/godoc/vfs/mapfs"
18+
"golang.org/x/tools/internal/typeparams"
1619
)
1720

1821
// TestIgnoredGoFiles tests the scenario where a folder has no .go or .c files,
@@ -128,3 +131,115 @@ func TestMarkdown(t *testing.T) {
128131
testServeBody(t, p, "/doc/test.html", "<strong>bold</strong>")
129132
testServeBody(t, p, "/doc/test2.html", "<em>template</em>")
130133
}
134+
135+
func TestGenerics(t *testing.T) {
136+
if !typeparams.Enabled {
137+
t.Skip("type params are not enabled at this Go version")
138+
}
139+
140+
c := NewCorpus(mapfs.New(map[string]string{
141+
"blah/blah.go": `package blah
142+
143+
var A AStruct[int]
144+
145+
type AStruct[T any] struct {
146+
A string
147+
X T
148+
}
149+
150+
func (a *AStruct[T]) Method() T {
151+
return a.X
152+
}
153+
154+
func (a AStruct[T]) NonPointerMethod() T {
155+
return a.X
156+
}
157+
158+
func NewAStruct[T any](arg T) *AStruct[T] {
159+
return &AStruct[T]{ X: arg }
160+
}
161+
162+
type NonGenericStruct struct {
163+
B int
164+
}
165+
166+
func (b *NonGenericStruct) NonGenericMethod() int {
167+
return b.B
168+
}
169+
170+
func NewNonGenericStruct(arg int) *NonGenericStruct {
171+
return &NonGenericStruct{arg}
172+
}
173+
174+
type Pair[K, V any] struct {
175+
K K
176+
V V
177+
}
178+
179+
func (p Pair[K, V]) Apply(kf func(K) K, vf func(V) V) Pair[K, V] {
180+
return &Pair{ K: kf(p.K), V: vf(p.V) }
181+
}
182+
183+
func (p *Pair[K, V]) Set(k K, v V) {
184+
p.K = k
185+
p.V = v
186+
}
187+
188+
func NewPair[K, V any](k K, v V) Pair[K, V] {
189+
return Pair[K, V]{ k, v }
190+
}
191+
`}))
192+
193+
srv := &handlerServer{
194+
p: &Presentation{
195+
Corpus: c,
196+
},
197+
c: c,
198+
}
199+
pInfo := srv.GetPageInfo("/blah/", "", NoFiltering, "linux", "amd64")
200+
t.Logf("%v\n", pInfo)
201+
202+
findType := func(name string) *doc.Type {
203+
for _, typ := range pInfo.PDoc.Types {
204+
if typ.Name == name {
205+
return typ
206+
}
207+
}
208+
return nil
209+
}
210+
211+
assertFuncs := func(typ *doc.Type, typFuncs []*doc.Func, funcs ...string) {
212+
typfuncs := make([]string, len(typFuncs))
213+
for i := range typFuncs {
214+
typfuncs[i] = typFuncs[i].Name
215+
}
216+
sort.Strings(typfuncs)
217+
sort.Strings(funcs)
218+
if len(typfuncs) != len(funcs) {
219+
t.Errorf("function mismatch for type %q, got: %q, want: %q", typ.Name, typfuncs, funcs)
220+
return
221+
}
222+
for i := range funcs {
223+
if funcs[i] != typfuncs[i] {
224+
t.Errorf("function mismatch for type %q: got: %q, want: %q", typ.Name, typfuncs, funcs)
225+
return
226+
}
227+
}
228+
}
229+
230+
aStructType := findType("AStruct")
231+
assertFuncs(aStructType, aStructType.Funcs, "NewAStruct")
232+
assertFuncs(aStructType, aStructType.Methods, "Method", "NonPointerMethod")
233+
234+
nonGenericStructType := findType("NonGenericStruct")
235+
assertFuncs(nonGenericStructType, nonGenericStructType.Funcs, "NewNonGenericStruct")
236+
assertFuncs(nonGenericStructType, nonGenericStructType.Methods, "NonGenericMethod")
237+
238+
pairType := findType("Pair")
239+
assertFuncs(pairType, pairType.Funcs, "NewPair")
240+
assertFuncs(pairType, pairType.Methods, "Apply", "Set")
241+
242+
if len(pInfo.PDoc.Funcs) > 0 {
243+
t.Errorf("unexpected functions in package documentation")
244+
}
245+
}

0 commit comments

Comments
 (0)