From cda4201ef0e15faa28351d969b3b17c5fcfe375c Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Wed, 9 Feb 2022 17:07:05 -0500 Subject: [PATCH] internal/lsp/source: simplify Identifier.enclosing Receiver base types are either *Interface or *Named, never *Struct. Absent a comment, I don't know why we had handling for *Struct, so just delete it. We only care about the enclosing type if it is named, so track a *TypeName rather than an arbitrary Type. Change-Id: I729c85b935a35da429b975327b56d81dc56773cf Reviewed-on: https://go-review.googlesource.com/c/tools/+/384696 Trust: Robert Findley Run-TryBot: Robert Findley Reviewed-by: Heschi Kreinick gopls-CI: kokoro TryBot-Result: Gopher Robot --- internal/lsp/source/hover.go | 32 +++++++++++++------------- internal/lsp/source/identifier.go | 23 ++++++++++-------- internal/lsp/source/identifier_test.go | 6 ++--- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/internal/lsp/source/hover.go b/internal/lsp/source/hover.go index a4d3c77830..d95c322f96 100644 --- a/internal/lsp/source/hover.go +++ b/internal/lsp/source/hover.go @@ -329,37 +329,37 @@ func HoverIdentifier(ctx context.Context, i *IdentifierInfo) (*HoverInformation, if !obj.Exported() { return h, nil } - var rTypeName string + var recvName string // receiver type name + switch obj := obj.(type) { case *types.Var: // If the object is a field, and we have an associated selector // composite literal, or struct, we can determine the link. - if obj.IsField() { - if named, ok := i.enclosing.(*types.Named); ok { - rTypeName = named.Obj().Name() - } + if obj.IsField() && i.enclosing != nil { + recvName = i.enclosing.Name() } case *types.Func: typ, ok := obj.Type().(*types.Signature) if !ok { - return h, nil + // Note: this should never happen. go/types guarantees that the type of + // *Funcs are Signatures. + // + // TODO(rfindley): given a 'debug' mode, we should panic here. + return nil, fmt.Errorf("BUG: incorrect type %T for *Func", obj.Type()) } if r := typ.Recv(); r != nil { - switch rtyp := Deref(r.Type()).(type) { - case *types.Struct: - rTypeName = r.Name() - case *types.Named: + if rtyp, _ := Deref(r.Type()).(*types.Named); rtyp != nil { // If we have an unexported type, see if the enclosing type is // exported (we may have an interface or struct we can link // to). If not, don't show any link. if !rtyp.Obj().Exported() { - if named, ok := i.enclosing.(*types.Named); ok && named.Obj().Exported() { - rTypeName = named.Obj().Name() + if i.enclosing != nil && i.enclosing.Exported() { + recvName = i.enclosing.Name() } else { return h, nil } } else { - rTypeName = rtyp.Obj().Name() + recvName = rtyp.Obj().Name() } } } @@ -373,9 +373,9 @@ func HoverIdentifier(ctx context.Context, i *IdentifierInfo) (*HoverInformation, if mod, version, ok := moduleAtVersion(h.LinkPath, i); ok { h.LinkPath = strings.Replace(h.LinkPath, mod, mod+"@"+version, 1) } - if rTypeName != "" { - h.LinkAnchor = fmt.Sprintf("%s.%s", rTypeName, obj.Name()) - h.symbolName = fmt.Sprintf("(%s.%s).%s", obj.Pkg().Name(), rTypeName, obj.Name()) + if recvName != "" { + h.LinkAnchor = fmt.Sprintf("%s.%s", recvName, obj.Name()) + h.symbolName = fmt.Sprintf("(%s.%s).%s", obj.Pkg().Name(), recvName, obj.Name()) return h, nil } // For most cases, the link is "package/path#symbol". diff --git a/internal/lsp/source/identifier.go b/internal/lsp/source/identifier.go index e845b906fb..bf4941f189 100644 --- a/internal/lsp/source/identifier.go +++ b/internal/lsp/source/identifier.go @@ -39,9 +39,10 @@ type IdentifierInfo struct { ident *ast.Ident - // enclosing is an expression used to determine the link anchor for an - // identifier. If it's a named type, it should be exported. - enclosing types.Type + // For struct fields or embedded interfaces, enclosing is the object + // corresponding to the outer type declaration, if it is exported, for use in + // documentation links. + enclosing *types.TypeName pkg Package qf types.Qualifier @@ -375,7 +376,7 @@ func inferredSignature(info *types.Info, id *ast.Ident) *types.Signature { return sig } -func searchForEnclosing(info *types.Info, path []ast.Node) types.Type { +func searchForEnclosing(info *types.Info, path []ast.Node) *types.TypeName { for _, n := range path { switch n := n.(type) { case *ast.SelectorExpr: @@ -383,9 +384,9 @@ func searchForEnclosing(info *types.Info, path []ast.Node) types.Type { recv := Deref(sel.Recv()) // Keep track of the last exported type seen. - var exported types.Type + var exported *types.TypeName if named, ok := recv.(*types.Named); ok && named.Obj().Exported() { - exported = named + exported = named.Obj() } // We don't want the last element, as that's the field or // method itself. @@ -393,7 +394,7 @@ func searchForEnclosing(info *types.Info, path []ast.Node) types.Type { if r, ok := recv.Underlying().(*types.Struct); ok { recv = Deref(r.Field(index).Type()) if named, ok := recv.(*types.Named); ok && named.Obj().Exported() { - exported = named + exported = named.Obj() } } } @@ -401,12 +402,16 @@ func searchForEnclosing(info *types.Info, path []ast.Node) types.Type { } case *ast.CompositeLit: if t, ok := info.Types[n]; ok { - return t.Type + if named, _ := t.Type.(*types.Named); named != nil { + return named.Obj() + } } case *ast.TypeSpec: if _, ok := n.Type.(*ast.StructType); ok { if t, ok := info.Defs[n.Name]; ok { - return t.Type() + if tname, _ := t.(*types.TypeName); tname != nil { + return tname + } } } } diff --git a/internal/lsp/source/identifier_test.go b/internal/lsp/source/identifier_test.go index 5e191e45fb..9bbdf58def 100644 --- a/internal/lsp/source/identifier_test.go +++ b/internal/lsp/source/identifier_test.go @@ -83,14 +83,14 @@ func TestSearchForEnclosing(t *testing.T) { if _, err = (*types.Config)(nil).Check("p", fset, []*ast.File{file}, info); err != nil { t.Fatal(err) } - typ := searchForEnclosing(info, path) - if typ == nil { + obj := searchForEnclosing(info, path) + if obj == nil { if test.wantTypeName != "" { t.Errorf("searchForEnclosing(...) = , want %q", test.wantTypeName) } return } - if got := typ.(*types.Named).Obj().Name(); got != test.wantTypeName { + if got := obj.Name(); got != test.wantTypeName { t.Errorf("searchForEnclosing(...) = %q, want %q", got, test.wantTypeName) } })