Skip to content

Commit

Permalink
godoc: fix addNames for generics
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
aarzilli authored and findleyr committed Jan 6, 2022
1 parent 548d57a commit 6d8748d
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 4 deletions.
16 changes: 12 additions & 4 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"golang.org/x/tools/godoc/analysis"
"golang.org/x/tools/godoc/util"
"golang.org/x/tools/godoc/vfs"
"golang.org/x/tools/internal/typeparams"
)

// handlerServer is a migration from an old godoc http Handler type.
Expand Down Expand Up @@ -462,12 +463,19 @@ func addNames(names map[string]bool, decl ast.Decl) {
case *ast.FuncDecl:
name := d.Name.Name
if d.Recv != nil {
r := d.Recv.List[0].Type
if rr, isstar := r.(*ast.StarExpr); isstar {
r = rr.X
}

var typeName string
switch r := d.Recv.List[0].Type.(type) {
case *ast.StarExpr:
typeName = r.X.(*ast.Ident).Name
switch x := r.(type) {
case *ast.Ident:
typeName = r.Name
typeName = x.Name
case *ast.IndexExpr:
typeName = x.X.(*ast.Ident).Name
case *typeparams.IndexListExpr:
typeName = x.X.(*ast.Ident).Name
}
name = typeName + "_" + name
}
Expand Down
115 changes: 115 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
package godoc

import (
"go/doc"
"net/http"
"net/http/httptest"
"net/url"
"sort"
"strings"
"testing"
"text/template"

"golang.org/x/tools/godoc/vfs/mapfs"
"golang.org/x/tools/internal/typeparams"
)

// TestIgnoredGoFiles tests the scenario where a folder has no .go or .c files,
Expand Down Expand Up @@ -128,3 +131,115 @@ func TestMarkdown(t *testing.T) {
testServeBody(t, p, "/doc/test.html", "<strong>bold</strong>")
testServeBody(t, p, "/doc/test2.html", "<em>template</em>")
}

func TestGenerics(t *testing.T) {
if !typeparams.Enabled {
t.Skip("type params are not enabled at this Go version")
}

c := NewCorpus(mapfs.New(map[string]string{
"blah/blah.go": `package blah
var A AStruct[int]
type AStruct[T any] struct {
A string
X T
}
func (a *AStruct[T]) Method() T {
return a.X
}
func (a AStruct[T]) NonPointerMethod() T {
return a.X
}
func NewAStruct[T any](arg T) *AStruct[T] {
return &AStruct[T]{ X: arg }
}
type NonGenericStruct struct {
B int
}
func (b *NonGenericStruct) NonGenericMethod() int {
return b.B
}
func NewNonGenericStruct(arg int) *NonGenericStruct {
return &NonGenericStruct{arg}
}
type Pair[K, V any] struct {
K K
V V
}
func (p Pair[K, V]) Apply(kf func(K) K, vf func(V) V) Pair[K, V] {
return &Pair{ K: kf(p.K), V: vf(p.V) }
}
func (p *Pair[K, V]) Set(k K, v V) {
p.K = k
p.V = v
}
func NewPair[K, V any](k K, v V) Pair[K, V] {
return Pair[K, V]{ k, v }
}
`}))

srv := &handlerServer{
p: &Presentation{
Corpus: c,
},
c: c,
}
pInfo := srv.GetPageInfo("/blah/", "", NoFiltering, "linux", "amd64")
t.Logf("%v\n", pInfo)

findType := func(name string) *doc.Type {
for _, typ := range pInfo.PDoc.Types {
if typ.Name == name {
return typ
}
}
return nil
}

assertFuncs := func(typ *doc.Type, typFuncs []*doc.Func, funcs ...string) {
typfuncs := make([]string, len(typFuncs))
for i := range typFuncs {
typfuncs[i] = typFuncs[i].Name
}
sort.Strings(typfuncs)
sort.Strings(funcs)
if len(typfuncs) != len(funcs) {
t.Errorf("function mismatch for type %q, got: %q, want: %q", typ.Name, typfuncs, funcs)
return
}
for i := range funcs {
if funcs[i] != typfuncs[i] {
t.Errorf("function mismatch for type %q: got: %q, want: %q", typ.Name, typfuncs, funcs)
return
}
}
}

aStructType := findType("AStruct")
assertFuncs(aStructType, aStructType.Funcs, "NewAStruct")
assertFuncs(aStructType, aStructType.Methods, "Method", "NonPointerMethod")

nonGenericStructType := findType("NonGenericStruct")
assertFuncs(nonGenericStructType, nonGenericStructType.Funcs, "NewNonGenericStruct")
assertFuncs(nonGenericStructType, nonGenericStructType.Methods, "NonGenericMethod")

pairType := findType("Pair")
assertFuncs(pairType, pairType.Funcs, "NewPair")
assertFuncs(pairType, pairType.Methods, "Apply", "Set")

if len(pInfo.PDoc.Funcs) > 0 {
t.Errorf("unexpected functions in package documentation")
}
}

0 comments on commit 6d8748d

Please sign in to comment.