From: Damien Neil Date: Thu, 10 Jul 2025 22:43:43 +0000 (-0700) Subject: go/doc: linkify interface methods X-Git-Tag: go1.26rc1~982 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=1eec830f54;p=gostls13.git go/doc: linkify interface methods Create doc links for references to interface methods, such as "[Context.Err]". This does not attempt to handle embedded interfaces: The linked-to method must be declared within the named type. Fixes #54033 Change-Id: I4d7a132affe682c85958ca785bcc96571404e6c1 Reviewed-on: https://go-review.googlesource.com/c/go/+/687395 Reviewed-by: Robert Findley Auto-Submit: Damien Neil Reviewed-by: Sean Liao LUCI-TryBot-Result: Go LUCI --- diff --git a/src/go/doc/comment_test.go b/src/go/doc/comment_test.go index 004ae9d13d..0e7de3eb78 100644 --- a/src/go/doc/comment_test.go +++ b/src/go/doc/comment_test.go @@ -24,12 +24,12 @@ func TestComment(t *testing.T) { pkg := New(pkgs["pkgdoc"], "testdata/pkgdoc", 0) var ( - input = "[T] and [U] are types, and [T.M] is a method, but [V] is a broken link. [rand.Int] and [crand.Reader] are things. [G.M1] and [G.M2] are generic methods.\n" - wantHTML = `

T and U are types, and T.M is a method, but [V] is a broken link. rand.Int and crand.Reader are things. G.M1 and G.M2 are generic methods.` + "\n" - wantOldHTML = "

[T] and [U] are types, and [T.M] is a method, but [V] is a broken link. [rand.Int] and [crand.Reader] are things. [G.M1] and [G.M2] are generic methods.\n" - wantMarkdown = "[T](#T) and [U](#U) are types, and [T.M](#T.M) is a method, but \\[V] is a broken link. [rand.Int](/math/rand#Int) and [crand.Reader](/crypto/rand#Reader) are things. [G.M1](#G.M1) and [G.M2](#G.M2) are generic methods.\n" - wantText = "T and U are types, and T.M is a method, but [V] is a broken link. rand.Int and\ncrand.Reader are things. G.M1 and G.M2 are generic methods.\n" - wantOldText = "[T] and [U] are types, and [T.M] is a method, but [V] is a broken link.\n[rand.Int] and [crand.Reader] are things. [G.M1] and [G.M2] are generic methods.\n" + input = "[T] and [U] are types, and [T.M] is a method, but [V] is a broken link. [rand.Int] and [crand.Reader] are things. [G.M1] and [G.M2] are generic methods. [I.F] is an interface method and [I.V] is a broken link.\n" + wantHTML = `

T and U are types, and T.M is a method, but [V] is a broken link. rand.Int and crand.Reader are things. G.M1 and G.M2 are generic methods. I.F is an interface method and [I.V] is a broken link.` + "\n" + wantOldHTML = "

[T] and [U] are types, and [T.M] is a method, but [V] is a broken link. [rand.Int] and [crand.Reader] are things. [G.M1] and [G.M2] are generic methods. [I.F] is an interface method and [I.V] is a broken link.\n" + wantMarkdown = "[T](#T) and [U](#U) are types, and [T.M](#T.M) is a method, but \\[V] is a broken link. [rand.Int](/math/rand#Int) and [crand.Reader](/crypto/rand#Reader) are things. [G.M1](#G.M1) and [G.M2](#G.M2) are generic methods. [I.F](#I.F) is an interface method and \\[I.V] is a broken link.\n" + wantText = "T and U are types, and T.M is a method, but [V] is a broken link. rand.Int and\ncrand.Reader are things. G.M1 and G.M2 are generic methods. I.F is an interface\nmethod and [I.V] is a broken link.\n" + wantOldText = "[T] and [U] are types, and [T.M] is a method, but [V] is a broken link.\n[rand.Int] and [crand.Reader] are things. [G.M1] and [G.M2] are generic methods.\n[I.F] is an interface method and [I.V] is a broken link.\n" wantSynopsis = "T and U are types, and T.M is a method, but [V] is a broken link." wantOldSynopsis = "[T] and [U] are types, and [T.M] is a method, but [V] is a broken link." ) diff --git a/src/go/doc/doc.go b/src/go/doc/doc.go index f7e3c1bad8..0c23f1a46c 100644 --- a/src/go/doc/doc.go +++ b/src/go/doc/doc.go @@ -167,6 +167,7 @@ func (p *Package) collectTypes(types []*Type) { p.collectValues(t.Vars) p.collectFuncs(t.Funcs) p.collectFuncs(t.Methods) + p.collectInterfaceMethods(t) } } @@ -184,6 +185,33 @@ func (p *Package) collectFuncs(funcs []*Func) { } } +// collectInterfaceMethods adds methods of interface types within t to p.syms. +// Note that t.Methods will contain methods of non-interface types, but not interface types. +// Adding interface methods to t.Methods might make sense, but would cause us to +// include those methods in the documentation index. Adding interface methods to p.syms +// here allows us to linkify references like [io.Reader.Read] without making any other +// changes to the documentation formatting at this time. +// +// If we do start adding interface methods to t.Methods in the future, +// collectInterfaceMethods can be dropped as redundant with collectFuncs(t.Methods). +func (p *Package) collectInterfaceMethods(t *Type) { + for _, s := range t.Decl.Specs { + spec, ok := s.(*ast.TypeSpec) + if !ok { + continue + } + list, isStruct := fields(spec.Type) + if isStruct { + continue + } + for _, field := range list { + for _, name := range field.Names { + p.syms[t.Name+"."+name.Name] = true + } + } + } +} + // NewFromFiles computes documentation for a package. // // The package is specified by a list of *ast.Files and corresponding diff --git a/src/go/doc/testdata/pkgdoc/doc.go b/src/go/doc/testdata/pkgdoc/doc.go index 3f822c7554..d542dc2cdd 100644 --- a/src/go/doc/testdata/pkgdoc/doc.go +++ b/src/go/doc/testdata/pkgdoc/doc.go @@ -20,5 +20,9 @@ var _ = crand.Reader type G[T any] struct{ x T } -func (g G[T]) M1() {} +func (g G[T]) M1() {} func (g *G[T]) M2() {} + +type I interface { + F() +}