diff --git a/internal/lsp/source/hover.go b/internal/lsp/source/hover.go index e8db266f88..a4d3c77830 100644 --- a/internal/lsp/source/hover.go +++ b/internal/lsp/source/hover.go @@ -309,7 +309,8 @@ func HoverIdentifier(ctx context.Context, i *IdentifierInfo) (*HoverInformation, h.LinkPath = strings.Replace(h.LinkPath, mod, mod+"@"+version, 1) } return h, nil - case *types.Builtin: + } + if obj.Parent() == types.Universe { h.importPath = "builtin" h.LinkPath = h.importPath h.LinkAnchor = obj.Name() @@ -520,10 +521,8 @@ func HoverInfo(ctx context.Context, s Snapshot, pkg Package, obj types.Object, p } case *ast.TypeSpec: if obj.Parent() == types.Universe { - if obj.Name() == "error" { - info = &HoverInformation{source: node} - } else { - info = &HoverInformation{source: node.Name} // comments not needed for builtins + if genDecl, ok := fullDecl.(*ast.GenDecl); ok { + info = formatTypeSpec(node, genDecl) } } case *ast.FuncDecl: diff --git a/internal/lsp/source/identifier.go b/internal/lsp/source/identifier.go index f8fddac1ec..e845b906fb 100644 --- a/internal/lsp/source/identifier.go +++ b/internal/lsp/source/identifier.go @@ -217,6 +217,10 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, pgf *Pa return nil, errors.Errorf("no declaration for %s", result.Name) } result.Declaration.node = decl + if typeSpec, ok := decl.(*ast.TypeSpec); ok { + // Find the GenDecl (which has the doc comments) for the TypeSpec. + result.Declaration.fullDecl = findGenDecl(builtin.File, typeSpec) + } // The builtin package isn't in the dependency graph, so the usual // utilities won't work here. @@ -314,6 +318,18 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, pgf *Pa return result, nil } +// findGenDecl determines the parent ast.GenDecl for a given ast.Spec. +func findGenDecl(f *ast.File, spec ast.Spec) *ast.GenDecl { + for _, decl := range f.Decls { + if genDecl, ok := decl.(*ast.GenDecl); ok { + if genDecl.Pos() <= spec.Pos() && genDecl.End() >= spec.End() { + return genDecl + } + } + } + return nil +} + // fullNode tries to extract the full spec corresponding to obj's declaration. // If the package was not parsed in full, the declaration file will be // re-parsed to ensure it has complete syntax. diff --git a/internal/lsp/testdata/godef/a/a.go.golden b/internal/lsp/testdata/godef/a/a.go.golden index 182928eebb..e8ce9079a9 100644 --- a/internal/lsp/testdata/godef/a/a.go.golden +++ b/internal/lsp/testdata/godef/a/a.go.golden @@ -164,8 +164,12 @@ func(t Type, size ...IntegerType) Type The make built\-in function allocates and initializes an object of type slice, map, or chan \(only\)\. -- string-hoverdef -- ```go -string +type string string ``` + +[`string` on pkg.go.dev](https://pkg.go.dev/builtin?utm_source=gopls#string) + +string is the set of all strings of 8\-bit bytes, conventionally but not necessarily representing UTF\-8\-encoded text\. -- typesImport-hoverdef -- ```go package types ("go/types")