internal/pkgdoc: handle IndexListExpr
This fixes the panic occuring when the documentation generator
(pkgdoc) processes methods defined on generic types with multiple
type parameters (e.g., func (T[A, B]) M() {}).
The pkg.go.dev (pkgsite) is the default documentation serer and
go.dev/pkg/... are redirected to pkg.go.dev/....
However, the pkgdoc package is still used in certain cases:
- golang.google.cn serves package documentation directly using pkgdoc.
- when explicitly requested via `?m=old` (https://go.dev/pkg/fmt/?m=old)
- local development
Fixes golang/go#80430
Change-Id: I4d12c7d20ca74da4f95eb40dbb4f5e964373eeef
Reviewed-on: https://go-review.googlesource.com/c/website/+/801540
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/internal/pkgdoc/doc.go b/internal/pkgdoc/doc.go
index 75e556d..3ed6c3a 100644
--- a/internal/pkgdoc/doc.go
+++ b/internal/pkgdoc/doc.go
@@ -356,6 +356,8 @@
}
if index, ok := r.(*ast.IndexExpr); ok { // Name[T]
r = index.X
+ } else if indexList, ok := r.(*ast.IndexListExpr); ok { // Name[T1, T2]
+ r = indexList.X
}
name = r.(*ast.Ident).Name + "_" + name
}
diff --git a/internal/pkgdoc/doc_test.go b/internal/pkgdoc/doc_test.go
index c28aa79..8fc0116 100644
--- a/internal/pkgdoc/doc_test.go
+++ b/internal/pkgdoc/doc_test.go
@@ -75,3 +75,43 @@
t.Errorf("pInfo.PDoc.Funcs[0].Doc = %q; want %q", got, want)
}
}
+
+func TestGenericMethodWithMultipleTypeParams(t *testing.T) {
+ const packagePath = "example.com/p"
+ fs := fstest.MapFS{
+ "lib/godoc/x.html": {},
+ "src/" + packagePath + "/p.go": {Data: []byte(`package p
+type T[A, B any] struct{}
+func (T[A, B]) M() {}
+`)},
+ }
+
+ site := web.NewSite(fs)
+ h, err := NewServer(fs, site, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ d := h.(*docs)
+
+ pInfo := d.open("src/"+packagePath, 0, "linux", "amd64") // must not panic
+ if pInfo.Err != nil {
+ t.Fatal(pInfo.Err)
+ }
+ if pInfo.PDoc == nil {
+ t.Fatal("pInfo.PDoc = nil; want non-nil")
+ }
+ if len(pInfo.PDoc.Types) != 1 {
+ t.Fatalf("len(pInfo.PDoc.Types) = %d; want 1", len(pInfo.PDoc.Types))
+ }
+ typ := pInfo.PDoc.Types[0]
+ if got, want := typ.Name, "T"; got != want {
+ t.Errorf("typ.Name = %q; want %q", got, want)
+ }
+ if len(typ.Methods) != 1 {
+ t.Fatalf("len(typ.Methods) = %d; want 1", len(typ.Methods))
+ }
+ meth := typ.Methods[0]
+ if got, want := meth.Name, "M"; got != want {
+ t.Errorf("meth.Name = %q; want %q", got, want)
+ }
+}