golang.org/x/tools/gopls@v0.15.3/internal/golang/hover.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package golang
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"go/ast"
    13  	"go/constant"
    14  	"go/doc"
    15  	"go/format"
    16  	"go/token"
    17  	"go/types"
    18  	"io/fs"
    19  	"path/filepath"
    20  	"strconv"
    21  	"strings"
    22  	"text/tabwriter"
    23  	"time"
    24  	"unicode/utf8"
    25  
    26  	"golang.org/x/text/unicode/runenames"
    27  	"golang.org/x/tools/go/ast/astutil"
    28  	"golang.org/x/tools/go/types/typeutil"
    29  	"golang.org/x/tools/gopls/internal/cache"
    30  	"golang.org/x/tools/gopls/internal/cache/metadata"
    31  	"golang.org/x/tools/gopls/internal/cache/parsego"
    32  	"golang.org/x/tools/gopls/internal/file"
    33  	"golang.org/x/tools/gopls/internal/protocol"
    34  	"golang.org/x/tools/gopls/internal/settings"
    35  	"golang.org/x/tools/gopls/internal/util/bug"
    36  	"golang.org/x/tools/gopls/internal/util/safetoken"
    37  	"golang.org/x/tools/gopls/internal/util/slices"
    38  	"golang.org/x/tools/gopls/internal/util/typesutil"
    39  	"golang.org/x/tools/internal/event"
    40  	"golang.org/x/tools/internal/tokeninternal"
    41  )
    42  
    43  // hoverJSON contains the structured result of a hover query. It is
    44  // formatted in one of several formats as determined by the HoverKind
    45  // setting, one of which is JSON.
    46  //
    47  // We believe this is used only by govim.
    48  // TODO(adonovan): see if we can wean all clients of this interface.
    49  type hoverJSON struct {
    50  	// Synopsis is a single sentence synopsis of the symbol's documentation.
    51  	Synopsis string `json:"synopsis"`
    52  
    53  	// FullDocumentation is the symbol's full documentation.
    54  	FullDocumentation string `json:"fullDocumentation"`
    55  
    56  	// Signature is the symbol's signature.
    57  	Signature string `json:"signature"`
    58  
    59  	// SingleLine is a single line describing the symbol.
    60  	// This is recommended only for use in clients that show a single line for hover.
    61  	SingleLine string `json:"singleLine"`
    62  
    63  	// SymbolName is the human-readable name to use for the symbol in links.
    64  	SymbolName string `json:"symbolName"`
    65  
    66  	// LinkPath is the pkg.go.dev link for the given symbol.
    67  	// For example, the "go/ast" part of "pkg.go.dev/go/ast#Node".
    68  	LinkPath string `json:"linkPath"`
    69  
    70  	// LinkAnchor is the pkg.go.dev link anchor for the given symbol.
    71  	// For example, the "Node" part of "pkg.go.dev/go/ast#Node".
    72  	LinkAnchor string `json:"linkAnchor"`
    73  
    74  	// New fields go below, and are unexported. The existing
    75  	// exported fields are underspecified and have already
    76  	// constrained our movements too much. A detailed JSON
    77  	// interface might be nice, but it needs a design and a
    78  	// precise specification.
    79  
    80  	// typeDecl is the declaration syntax for a type,
    81  	// or "" for a non-type.
    82  	typeDecl string
    83  
    84  	// methods is the list of descriptions of methods of a type,
    85  	// omitting any that are obvious from typeDecl.
    86  	// It is "" for a non-type.
    87  	methods string
    88  
    89  	// promotedFields is the list of descriptions of accessible
    90  	// fields of a (struct) type that were promoted through an
    91  	// embedded field.
    92  	promotedFields string
    93  }
    94  
    95  // Hover implements the "textDocument/hover" RPC for Go files.
    96  func Hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, position protocol.Position) (*protocol.Hover, error) {
    97  	ctx, done := event.Start(ctx, "golang.Hover")
    98  	defer done()
    99  
   100  	rng, h, err := hover(ctx, snapshot, fh, position)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	if h == nil {
   105  		return nil, nil
   106  	}
   107  	hover, err := formatHover(h, snapshot.Options())
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	return &protocol.Hover{
   112  		Contents: protocol.MarkupContent{
   113  			Kind:  snapshot.Options().PreferredContentFormat,
   114  			Value: hover,
   115  		},
   116  		Range: rng,
   117  	}, nil
   118  }
   119  
   120  // hover computes hover information at the given position. If we do not support
   121  // hovering at the position, it returns _, nil, nil: an error is only returned
   122  // if the position is valid but we fail to compute hover information.
   123  func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position) (protocol.Range, *hoverJSON, error) {
   124  	pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
   125  	if err != nil {
   126  		return protocol.Range{}, nil, err
   127  	}
   128  	pos, err := pgf.PositionPos(pp)
   129  	if err != nil {
   130  		return protocol.Range{}, nil, err
   131  	}
   132  
   133  	// Handle hovering over import paths, which do not have an associated
   134  	// identifier.
   135  	for _, spec := range pgf.File.Imports {
   136  		// We are inclusive of the end point here to allow hovering when the cursor
   137  		// is just after the import path.
   138  		if spec.Path.Pos() <= pos && pos <= spec.Path.End() {
   139  			return hoverImport(ctx, snapshot, pkg, pgf, spec)
   140  		}
   141  	}
   142  
   143  	// Handle hovering over the package name, which does not have an associated
   144  	// object.
   145  	// As with import paths, we allow hovering just after the package name.
   146  	if pgf.File.Name != nil && pgf.File.Name.Pos() <= pos && pos <= pgf.File.Name.Pos() {
   147  		return hoverPackageName(pkg, pgf)
   148  	}
   149  
   150  	// Handle hovering over (non-import-path) literals.
   151  	if path, _ := astutil.PathEnclosingInterval(pgf.File, pos, pos); len(path) > 0 {
   152  		if lit, _ := path[0].(*ast.BasicLit); lit != nil {
   153  			return hoverLit(pgf, lit, pos)
   154  		}
   155  	}
   156  
   157  	// Handle hovering over embed directive argument.
   158  	pattern, embedRng := parseEmbedDirective(pgf.Mapper, pp)
   159  	if pattern != "" {
   160  		return hoverEmbed(fh, embedRng, pattern)
   161  	}
   162  
   163  	// Handle linkname directive by overriding what to look for.
   164  	var linkedRange *protocol.Range // range referenced by linkname directive, or nil
   165  	if pkgPath, name, offset := parseLinkname(pgf.Mapper, pp); pkgPath != "" && name != "" {
   166  		// rng covering 2nd linkname argument: pkgPath.name.
   167  		rng, err := pgf.PosRange(pgf.Tok.Pos(offset), pgf.Tok.Pos(offset+len(pkgPath)+len(".")+len(name)))
   168  		if err != nil {
   169  			return protocol.Range{}, nil, fmt.Errorf("range over linkname arg: %w", err)
   170  		}
   171  		linkedRange = &rng
   172  
   173  		pkg, pgf, pos, err = findLinkname(ctx, snapshot, PackagePath(pkgPath), name)
   174  		if err != nil {
   175  			return protocol.Range{}, nil, fmt.Errorf("find linkname: %w", err)
   176  		}
   177  	}
   178  
   179  	// The general case: compute hover information for the object referenced by
   180  	// the identifier at pos.
   181  	ident, obj, selectedType := referencedObject(pkg, pgf, pos)
   182  	if obj == nil || ident == nil {
   183  		return protocol.Range{}, nil, nil // no object to hover
   184  	}
   185  
   186  	// Unless otherwise specified, rng covers the ident being hovered.
   187  	var rng protocol.Range
   188  	if linkedRange != nil {
   189  		rng = *linkedRange
   190  	} else {
   191  		rng, err = pgf.NodeRange(ident)
   192  		if err != nil {
   193  			return protocol.Range{}, nil, err
   194  		}
   195  	}
   196  
   197  	// By convention, we qualify hover information relative to the package
   198  	// from which the request originated.
   199  	qf := typesutil.FileQualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo())
   200  
   201  	// Handle type switch identifiers as a special case, since they don't have an
   202  	// object.
   203  	//
   204  	// There's not much useful information to provide.
   205  	if selectedType != nil {
   206  		fakeObj := types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), selectedType)
   207  		signature := types.ObjectString(fakeObj, qf)
   208  		return rng, &hoverJSON{
   209  			Signature:  signature,
   210  			SingleLine: signature,
   211  			SymbolName: fakeObj.Name(),
   212  		}, nil
   213  	}
   214  
   215  	// Handle builtins, which don't have a package or position.
   216  	if !obj.Pos().IsValid() {
   217  		h, err := hoverBuiltin(ctx, snapshot, obj)
   218  		return rng, h, err
   219  	}
   220  
   221  	// For all other objects, consider the full syntax of their declaration in
   222  	// order to correctly compute their documentation, signature, and link.
   223  	//
   224  	// Beware: decl{PGF,Pos} are not necessarily associated with pkg.FileSet().
   225  	declPGF, declPos, err := parseFull(ctx, snapshot, pkg.FileSet(), obj.Pos())
   226  	if err != nil {
   227  		return protocol.Range{}, nil, fmt.Errorf("re-parsing declaration of %s: %v", obj.Name(), err)
   228  	}
   229  	decl, spec, field := findDeclInfo([]*ast.File{declPGF.File}, declPos) // may be nil^3
   230  	comment := chooseDocComment(decl, spec, field)
   231  	docText := comment.Text()
   232  
   233  	// By default, types.ObjectString provides a reasonable signature.
   234  	signature := objectString(obj, qf, declPos, declPGF.Tok, spec)
   235  	singleLineSignature := signature
   236  
   237  	// TODO(rfindley): we could do much better for inferred signatures.
   238  	if inferred := inferredSignature(pkg.GetTypesInfo(), ident); inferred != nil {
   239  		if s := inferredSignatureString(obj, qf, inferred); s != "" {
   240  			signature = s
   241  		}
   242  	}
   243  
   244  	var typeDecl, methods, fields string
   245  
   246  	// For "objects defined by a type spec", the signature produced by
   247  	// objectString is insufficient:
   248  	//  (1) large structs are formatted poorly, with no newlines
   249  	//  (2) we lose inline comments
   250  	// Furthermore, we include a summary of their method set.
   251  	_, isTypeName := obj.(*types.TypeName)
   252  	_, isTypeParam := obj.Type().(*types.TypeParam)
   253  	if isTypeName && !isTypeParam {
   254  		spec, ok := spec.(*ast.TypeSpec)
   255  		if !ok {
   256  			// We cannot find a TypeSpec for this type or alias declaration
   257  			// (that is not a type parameter or a built-in).
   258  			// This should be impossible even for ill-formed trees;
   259  			// we suspect that AST repair may be creating inconsistent
   260  			// positions. Don't report a bug in that case. (#64241)
   261  			errorf := fmt.Errorf
   262  			if !declPGF.Fixed() {
   263  				errorf = bug.Errorf
   264  			}
   265  			return protocol.Range{}, nil, errorf("type name %q without type spec", obj.Name())
   266  		}
   267  
   268  		// Format the type's declaration syntax.
   269  		{
   270  			// Don't duplicate comments.
   271  			spec2 := *spec
   272  			spec2.Doc = nil
   273  			spec2.Comment = nil
   274  
   275  			var b strings.Builder
   276  			b.WriteString("type ")
   277  			fset := tokeninternal.FileSetFor(declPGF.Tok)
   278  			// TODO(adonovan): use a smarter formatter that omits
   279  			// inaccessible fields (non-exported ones from other packages).
   280  			if err := format.Node(&b, fset, &spec2); err != nil {
   281  				return protocol.Range{}, nil, err
   282  			}
   283  			typeDecl = b.String()
   284  		}
   285  
   286  		// Promoted fields
   287  		//
   288  		// Show a table of accessible fields of the (struct)
   289  		// type that may not be visible in the syntax (above)
   290  		// due to promotion through embedded fields.
   291  		//
   292  		// Example:
   293  		//
   294  		//	// Embedded fields:
   295  		//	foo int	   // through x.y
   296  		//	z   string // through x.y
   297  		if prom := promotedFields(obj.Type(), pkg.GetTypes()); len(prom) > 0 {
   298  			var b strings.Builder
   299  			b.WriteString("// Embedded fields:\n")
   300  			w := tabwriter.NewWriter(&b, 0, 8, 1, ' ', 0)
   301  			for _, f := range prom {
   302  				fmt.Fprintf(w, "%s\t%s\t// through %s\t\n",
   303  					f.field.Name(),
   304  					types.TypeString(f.field.Type(), qf),
   305  					f.path)
   306  			}
   307  			w.Flush()
   308  			b.WriteByte('\n')
   309  			fields = b.String()
   310  		}
   311  
   312  		// -- methods --
   313  
   314  		// For an interface type, explicit methods will have
   315  		// already been displayed when the node was formatted
   316  		// above. Don't list these again.
   317  		var skip map[string]bool
   318  		if iface, ok := spec.Type.(*ast.InterfaceType); ok {
   319  			if iface.Methods.List != nil {
   320  				for _, m := range iface.Methods.List {
   321  					if len(m.Names) == 1 {
   322  						if skip == nil {
   323  							skip = make(map[string]bool)
   324  						}
   325  						skip[m.Names[0].Name] = true
   326  					}
   327  				}
   328  			}
   329  		}
   330  
   331  		// Display all the type's accessible methods,
   332  		// including those that require a pointer receiver,
   333  		// and those promoted from embedded struct fields or
   334  		// embedded interfaces.
   335  		var b strings.Builder
   336  		for _, m := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
   337  			if !accessibleTo(m.Obj(), pkg.GetTypes()) {
   338  				continue // inaccessible
   339  			}
   340  			if skip[m.Obj().Name()] {
   341  				continue // redundant with format.Node above
   342  			}
   343  			if b.Len() > 0 {
   344  				b.WriteByte('\n')
   345  			}
   346  
   347  			// Use objectString for its prettier rendering of method receivers.
   348  			b.WriteString(objectString(m.Obj(), qf, token.NoPos, nil, nil))
   349  		}
   350  		methods = b.String()
   351  
   352  		signature = typeDecl + "\n" + methods
   353  	}
   354  
   355  	// Compute link data (on pkg.go.dev or other documentation host).
   356  	//
   357  	// If linkPath is empty, the symbol is not linkable.
   358  	var (
   359  		linkName string            // => link title, always non-empty
   360  		linkPath string            // => link path
   361  		anchor   string            // link anchor
   362  		linkMeta *metadata.Package // metadata for the linked package
   363  	)
   364  	{
   365  		linkMeta = findFileInDeps(snapshot, pkg.Metadata(), declPGF.URI)
   366  		if linkMeta == nil {
   367  			return protocol.Range{}, nil, bug.Errorf("no package data for %s", declPGF.URI)
   368  		}
   369  
   370  		// For package names, we simply link to their imported package.
   371  		if pkgName, ok := obj.(*types.PkgName); ok {
   372  			linkName = pkgName.Name()
   373  			linkPath = pkgName.Imported().Path()
   374  			impID := linkMeta.DepsByPkgPath[PackagePath(pkgName.Imported().Path())]
   375  			linkMeta = snapshot.Metadata(impID)
   376  			if linkMeta == nil {
   377  				// Broken imports have fake package paths, so it is not a bug if we
   378  				// don't have metadata. As of writing, there is no way to distinguish
   379  				// broken imports from a true bug where expected metadata is missing.
   380  				return protocol.Range{}, nil, fmt.Errorf("no package data for %s", declPGF.URI)
   381  			}
   382  		} else {
   383  			// For all others, check whether the object is in the package scope, or
   384  			// an exported field or method of an object in the package scope.
   385  			//
   386  			// We try to match pkgsite's heuristics for what is linkable, and what is
   387  			// not.
   388  			var recv types.Object
   389  			switch obj := obj.(type) {
   390  			case *types.Func:
   391  				sig := obj.Type().(*types.Signature)
   392  				if sig.Recv() != nil {
   393  					tname := typeToObject(sig.Recv().Type())
   394  					if tname != nil { // beware typed nil
   395  						recv = tname
   396  					}
   397  				}
   398  			case *types.Var:
   399  				if obj.IsField() {
   400  					if spec, ok := spec.(*ast.TypeSpec); ok {
   401  						typeName := spec.Name
   402  						scopeObj, _ := obj.Pkg().Scope().Lookup(typeName.Name).(*types.TypeName)
   403  						if scopeObj != nil {
   404  							if st, _ := scopeObj.Type().Underlying().(*types.Struct); st != nil {
   405  								for i := 0; i < st.NumFields(); i++ {
   406  									if obj == st.Field(i) {
   407  										recv = scopeObj
   408  									}
   409  								}
   410  							}
   411  						}
   412  					}
   413  				}
   414  			}
   415  
   416  			// Even if the object is not available in package documentation, it may
   417  			// be embedded in a documented receiver. Detect this by searching
   418  			// enclosing selector expressions.
   419  			//
   420  			// TODO(rfindley): pkgsite doesn't document fields from embedding, just
   421  			// methods.
   422  			if recv == nil || !recv.Exported() {
   423  				path := pathEnclosingObjNode(pgf.File, pos)
   424  				if enclosing := searchForEnclosing(pkg.GetTypesInfo(), path); enclosing != nil {
   425  					recv = enclosing
   426  				} else {
   427  					recv = nil // note: just recv = ... could result in a typed nil.
   428  				}
   429  			}
   430  
   431  			pkg := obj.Pkg()
   432  			if recv != nil {
   433  				linkName = fmt.Sprintf("(%s.%s).%s", pkg.Name(), recv.Name(), obj.Name())
   434  				if obj.Exported() && recv.Exported() && pkg.Scope().Lookup(recv.Name()) == recv {
   435  					linkPath = pkg.Path()
   436  					anchor = fmt.Sprintf("%s.%s", recv.Name(), obj.Name())
   437  				}
   438  			} else {
   439  				linkName = fmt.Sprintf("%s.%s", pkg.Name(), obj.Name())
   440  				if obj.Exported() && pkg.Scope().Lookup(obj.Name()) == obj {
   441  					linkPath = pkg.Path()
   442  					anchor = obj.Name()
   443  				}
   444  			}
   445  		}
   446  	}
   447  
   448  	if snapshot.IsGoPrivatePath(linkPath) || linkMeta.ForTest != "" {
   449  		linkPath = ""
   450  	} else if linkMeta.Module != nil && linkMeta.Module.Version != "" {
   451  		mod := linkMeta.Module
   452  		linkPath = strings.Replace(linkPath, mod.Path, mod.Path+"@"+mod.Version, 1)
   453  	}
   454  
   455  	return rng, &hoverJSON{
   456  		Synopsis:          doc.Synopsis(docText),
   457  		FullDocumentation: docText,
   458  		SingleLine:        singleLineSignature,
   459  		SymbolName:        linkName,
   460  		Signature:         signature,
   461  		LinkPath:          linkPath,
   462  		LinkAnchor:        anchor,
   463  		typeDecl:          typeDecl,
   464  		methods:           methods,
   465  		promotedFields:    fields,
   466  	}, nil
   467  }
   468  
   469  // hoverBuiltin computes hover information when hovering over a builtin
   470  // identifier.
   471  func hoverBuiltin(ctx context.Context, snapshot *cache.Snapshot, obj types.Object) (*hoverJSON, error) {
   472  	// Special handling for error.Error, which is the only builtin method.
   473  	//
   474  	// TODO(rfindley): can this be unified with the handling below?
   475  	if obj.Name() == "Error" {
   476  		signature := obj.String()
   477  		return &hoverJSON{
   478  			Signature:  signature,
   479  			SingleLine: signature,
   480  			// TODO(rfindley): these are better than the current behavior.
   481  			// SymbolName: "(error).Error",
   482  			// LinkPath:   "builtin",
   483  			// LinkAnchor: "error.Error",
   484  		}, nil
   485  	}
   486  
   487  	pgf, node, err := builtinDecl(ctx, snapshot, obj)
   488  	if err != nil {
   489  		return nil, err
   490  	}
   491  
   492  	var comment *ast.CommentGroup
   493  	path, _ := astutil.PathEnclosingInterval(pgf.File, node.Pos(), node.End())
   494  	for _, n := range path {
   495  		switch n := n.(type) {
   496  		case *ast.GenDecl:
   497  			// Separate documentation and signature.
   498  			comment = n.Doc
   499  			node2 := *n
   500  			node2.Doc = nil
   501  			node = &node2
   502  		case *ast.FuncDecl:
   503  			// Ditto.
   504  			comment = n.Doc
   505  			node2 := *n
   506  			node2.Doc = nil
   507  			node = &node2
   508  		}
   509  	}
   510  
   511  	signature := FormatNodeFile(pgf.Tok, node)
   512  	// Replace fake types with their common equivalent.
   513  	// TODO(rfindley): we should instead use obj.Type(), which would have the
   514  	// *actual* types of the builtin call.
   515  	signature = replacer.Replace(signature)
   516  
   517  	docText := comment.Text()
   518  	return &hoverJSON{
   519  		Synopsis:          doc.Synopsis(docText),
   520  		FullDocumentation: docText,
   521  		Signature:         signature,
   522  		SingleLine:        obj.String(),
   523  		SymbolName:        obj.Name(),
   524  		LinkPath:          "builtin",
   525  		LinkAnchor:        obj.Name(),
   526  	}, nil
   527  }
   528  
   529  // hoverImport computes hover information when hovering over the import path of
   530  // imp in the file pgf of pkg.
   531  //
   532  // If we do not have metadata for the hovered import, it returns _
   533  func hoverImport(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Package, pgf *ParsedGoFile, imp *ast.ImportSpec) (protocol.Range, *hoverJSON, error) {
   534  	rng, err := pgf.NodeRange(imp.Path)
   535  	if err != nil {
   536  		return protocol.Range{}, nil, err
   537  	}
   538  
   539  	importPath := metadata.UnquoteImportPath(imp)
   540  	if importPath == "" {
   541  		return protocol.Range{}, nil, fmt.Errorf("invalid import path")
   542  	}
   543  	impID := pkg.Metadata().DepsByImpPath[importPath]
   544  	if impID == "" {
   545  		return protocol.Range{}, nil, fmt.Errorf("no package data for import %q", importPath)
   546  	}
   547  	impMetadata := snapshot.Metadata(impID)
   548  	if impMetadata == nil {
   549  		return protocol.Range{}, nil, bug.Errorf("failed to resolve import ID %q", impID)
   550  	}
   551  
   552  	// Find the first file with a package doc comment.
   553  	var comment *ast.CommentGroup
   554  	for _, f := range impMetadata.CompiledGoFiles {
   555  		fh, err := snapshot.ReadFile(ctx, f)
   556  		if err != nil {
   557  			if ctx.Err() != nil {
   558  				return protocol.Range{}, nil, ctx.Err()
   559  			}
   560  			continue
   561  		}
   562  		pgf, err := snapshot.ParseGo(ctx, fh, ParseHeader)
   563  		if err != nil {
   564  			if ctx.Err() != nil {
   565  				return protocol.Range{}, nil, ctx.Err()
   566  			}
   567  			continue
   568  		}
   569  		if pgf.File.Doc != nil {
   570  			comment = pgf.File.Doc
   571  			break
   572  		}
   573  	}
   574  
   575  	docText := comment.Text()
   576  	return rng, &hoverJSON{
   577  		Synopsis:          doc.Synopsis(docText),
   578  		FullDocumentation: docText,
   579  	}, nil
   580  }
   581  
   582  // hoverPackageName computes hover information for the package name of the file
   583  // pgf in pkg.
   584  func hoverPackageName(pkg *cache.Package, pgf *ParsedGoFile) (protocol.Range, *hoverJSON, error) {
   585  	var comment *ast.CommentGroup
   586  	for _, pgf := range pkg.CompiledGoFiles() {
   587  		if pgf.File.Doc != nil {
   588  			comment = pgf.File.Doc
   589  			break
   590  		}
   591  	}
   592  	rng, err := pgf.NodeRange(pgf.File.Name)
   593  	if err != nil {
   594  		return protocol.Range{}, nil, err
   595  	}
   596  	docText := comment.Text()
   597  	return rng, &hoverJSON{
   598  		Synopsis:          doc.Synopsis(docText),
   599  		FullDocumentation: docText,
   600  		// Note: including a signature is redundant, since the cursor is already on the
   601  		// package name.
   602  	}, nil
   603  }
   604  
   605  // hoverLit computes hover information when hovering over the basic literal lit
   606  // in the file pgf. The provided pos must be the exact position of the cursor,
   607  // as it is used to extract the hovered rune in strings.
   608  //
   609  // For example, hovering over "\u2211" in "foo \u2211 bar" yields:
   610  //
   611  //	'∑', U+2211, N-ARY SUMMATION
   612  func hoverLit(pgf *ParsedGoFile, lit *ast.BasicLit, pos token.Pos) (protocol.Range, *hoverJSON, error) {
   613  	var (
   614  		value      string    // if non-empty, a constant value to format in hover
   615  		r          rune      // if non-zero, format a description of this rune in hover
   616  		start, end token.Pos // hover span
   617  	)
   618  	// Extract a rune from the current position.
   619  	// 'Ω', "...Ω...", or 0x03A9 => 'Ω', U+03A9, GREEK CAPITAL LETTER OMEGA
   620  	switch lit.Kind {
   621  	case token.CHAR:
   622  		s, err := strconv.Unquote(lit.Value)
   623  		if err != nil {
   624  			// If the conversion fails, it's because of an invalid syntax, therefore
   625  			// there is no rune to be found.
   626  			return protocol.Range{}, nil, nil
   627  		}
   628  		r, _ = utf8.DecodeRuneInString(s)
   629  		if r == utf8.RuneError {
   630  			return protocol.Range{}, nil, fmt.Errorf("rune error")
   631  		}
   632  		start, end = lit.Pos(), lit.End()
   633  
   634  	case token.INT:
   635  		// Short literals (e.g. 99 decimal, 07 octal) are uninteresting.
   636  		if len(lit.Value) < 3 {
   637  			return protocol.Range{}, nil, nil
   638  		}
   639  
   640  		v := constant.MakeFromLiteral(lit.Value, lit.Kind, 0)
   641  		if v.Kind() != constant.Int {
   642  			return protocol.Range{}, nil, nil
   643  		}
   644  
   645  		switch lit.Value[:2] {
   646  		case "0x", "0X":
   647  			// As a special case, try to recognize hexadecimal literals as runes if
   648  			// they are within the range of valid unicode values.
   649  			if v, ok := constant.Int64Val(v); ok && v > 0 && v <= utf8.MaxRune && utf8.ValidRune(rune(v)) {
   650  				r = rune(v)
   651  			}
   652  			fallthrough
   653  		case "0o", "0O", "0b", "0B":
   654  			// Format the decimal value of non-decimal literals.
   655  			value = v.ExactString()
   656  			start, end = lit.Pos(), lit.End()
   657  		default:
   658  			return protocol.Range{}, nil, nil
   659  		}
   660  
   661  	case token.STRING:
   662  		// It's a string, scan only if it contains a unicode escape sequence under or before the
   663  		// current cursor position.
   664  		litOffset, err := safetoken.Offset(pgf.Tok, lit.Pos())
   665  		if err != nil {
   666  			return protocol.Range{}, nil, err
   667  		}
   668  		offset, err := safetoken.Offset(pgf.Tok, pos)
   669  		if err != nil {
   670  			return protocol.Range{}, nil, err
   671  		}
   672  		for i := offset - litOffset; i > 0; i-- {
   673  			// Start at the cursor position and search backward for the beginning of a rune escape sequence.
   674  			rr, _ := utf8.DecodeRuneInString(lit.Value[i:])
   675  			if rr == utf8.RuneError {
   676  				return protocol.Range{}, nil, fmt.Errorf("rune error")
   677  			}
   678  			if rr == '\\' {
   679  				// Got the beginning, decode it.
   680  				var tail string
   681  				r, _, tail, err = strconv.UnquoteChar(lit.Value[i:], '"')
   682  				if err != nil {
   683  					// If the conversion fails, it's because of an invalid syntax,
   684  					// therefore is no rune to be found.
   685  					return protocol.Range{}, nil, nil
   686  				}
   687  				// Only the rune escape sequence part of the string has to be highlighted, recompute the range.
   688  				runeLen := len(lit.Value) - (i + len(tail))
   689  				start = token.Pos(int(lit.Pos()) + i)
   690  				end = token.Pos(int(start) + runeLen)
   691  				break
   692  			}
   693  		}
   694  	}
   695  
   696  	if value == "" && r == 0 { // nothing to format
   697  		return protocol.Range{}, nil, nil
   698  	}
   699  
   700  	rng, err := pgf.PosRange(start, end)
   701  	if err != nil {
   702  		return protocol.Range{}, nil, err
   703  	}
   704  
   705  	var b strings.Builder
   706  	if value != "" {
   707  		b.WriteString(value)
   708  	}
   709  	if r != 0 {
   710  		runeName := runenames.Name(r)
   711  		if len(runeName) > 0 && runeName[0] == '<' {
   712  			// Check if the rune looks like an HTML tag. If so, trim the surrounding <>
   713  			// characters to work around https://github.com/microsoft/vscode/issues/124042.
   714  			runeName = strings.TrimRight(runeName[1:], ">")
   715  		}
   716  		if b.Len() > 0 {
   717  			b.WriteString(", ")
   718  		}
   719  		if strconv.IsPrint(r) {
   720  			fmt.Fprintf(&b, "'%c', ", r)
   721  		}
   722  		fmt.Fprintf(&b, "U+%04X, %s", r, runeName)
   723  	}
   724  	hover := b.String()
   725  	return rng, &hoverJSON{
   726  		Synopsis:          hover,
   727  		FullDocumentation: hover,
   728  	}, nil
   729  }
   730  
   731  // hoverEmbed computes hover information for a filepath.Match pattern.
   732  // Assumes that the pattern is relative to the location of fh.
   733  func hoverEmbed(fh file.Handle, rng protocol.Range, pattern string) (protocol.Range, *hoverJSON, error) {
   734  	s := &strings.Builder{}
   735  
   736  	dir := filepath.Dir(fh.URI().Path())
   737  	var matches []string
   738  	err := filepath.WalkDir(dir, func(abs string, d fs.DirEntry, e error) error {
   739  		if e != nil {
   740  			return e
   741  		}
   742  		rel, err := filepath.Rel(dir, abs)
   743  		if err != nil {
   744  			return err
   745  		}
   746  		ok, err := filepath.Match(pattern, rel)
   747  		if err != nil {
   748  			return err
   749  		}
   750  		if ok && !d.IsDir() {
   751  			matches = append(matches, rel)
   752  		}
   753  		return nil
   754  	})
   755  	if err != nil {
   756  		return protocol.Range{}, nil, err
   757  	}
   758  
   759  	for _, m := range matches {
   760  		// TODO: Renders each file as separate markdown paragraphs.
   761  		// If forcing (a single) newline is possible it might be more clear.
   762  		fmt.Fprintf(s, "%s\n\n", m)
   763  	}
   764  
   765  	json := &hoverJSON{
   766  		Signature:         fmt.Sprintf("Embedding %q", pattern),
   767  		Synopsis:          s.String(),
   768  		FullDocumentation: s.String(),
   769  	}
   770  	return rng, json, nil
   771  }
   772  
   773  // inferredSignatureString is a wrapper around the types.ObjectString function
   774  // that adds more information to inferred signatures. It will return an empty string
   775  // if the passed types.Object is not a signature.
   776  func inferredSignatureString(obj types.Object, qf types.Qualifier, inferred *types.Signature) string {
   777  	// If the signature type was inferred, prefer the inferred signature with a
   778  	// comment showing the generic signature.
   779  	if sig, _ := obj.Type().(*types.Signature); sig != nil && sig.TypeParams().Len() > 0 && inferred != nil {
   780  		obj2 := types.NewFunc(obj.Pos(), obj.Pkg(), obj.Name(), inferred)
   781  		str := types.ObjectString(obj2, qf)
   782  		// Try to avoid overly long lines.
   783  		if len(str) > 60 {
   784  			str += "\n"
   785  		} else {
   786  			str += " "
   787  		}
   788  		str += "// " + types.TypeString(sig, qf)
   789  		return str
   790  	}
   791  	return ""
   792  }
   793  
   794  // objectString is a wrapper around the types.ObjectString function.
   795  // It handles adding more information to the object string.
   796  // If spec is non-nil, it may be used to format additional declaration
   797  // syntax, and file must be the token.File describing its positions.
   798  //
   799  // Precondition: obj is not a built-in function or method.
   800  func objectString(obj types.Object, qf types.Qualifier, declPos token.Pos, file *token.File, spec ast.Spec) string {
   801  	str := types.ObjectString(obj, qf)
   802  
   803  	switch obj := obj.(type) {
   804  	case *types.Func:
   805  		// We fork ObjectString to improve its rendering of methods:
   806  		// specifically, we show the receiver name,
   807  		// and replace the period in (T).f by a space (#62190).
   808  
   809  		sig := obj.Type().(*types.Signature)
   810  
   811  		var buf bytes.Buffer
   812  		buf.WriteString("func ")
   813  		if recv := sig.Recv(); recv != nil {
   814  			buf.WriteByte('(')
   815  			if _, ok := recv.Type().(*types.Interface); ok {
   816  				// gcimporter creates abstract methods of
   817  				// named interfaces using the interface type
   818  				// (not the named type) as the receiver.
   819  				// Don't print it in full.
   820  				buf.WriteString("interface")
   821  			} else {
   822  				// Show receiver name (go/types does not).
   823  				name := recv.Name()
   824  				if name != "" && name != "_" {
   825  					buf.WriteString(name)
   826  					buf.WriteString(" ")
   827  				}
   828  				types.WriteType(&buf, recv.Type(), qf)
   829  			}
   830  			buf.WriteByte(')')
   831  			buf.WriteByte(' ') // space (go/types uses a period)
   832  		} else if s := qf(obj.Pkg()); s != "" {
   833  			buf.WriteString(s)
   834  			buf.WriteString(".")
   835  		}
   836  		buf.WriteString(obj.Name())
   837  		types.WriteSignature(&buf, sig, qf)
   838  		str = buf.String()
   839  
   840  	case *types.Const:
   841  		// Show value of a constant.
   842  		var (
   843  			declaration = obj.Val().String() // default formatted declaration
   844  			comment     = ""                 // if non-empty, a clarifying comment
   845  		)
   846  
   847  		// Try to use the original declaration.
   848  		switch obj.Val().Kind() {
   849  		case constant.String:
   850  			// Usually the original declaration of a string doesn't carry much information.
   851  			// Also strings can be very long. So, just use the constant's value.
   852  
   853  		default:
   854  			if spec, _ := spec.(*ast.ValueSpec); spec != nil {
   855  				for i, name := range spec.Names {
   856  					if declPos == name.Pos() {
   857  						if i < len(spec.Values) {
   858  							originalDeclaration := FormatNodeFile(file, spec.Values[i])
   859  							if originalDeclaration != declaration {
   860  								comment = declaration
   861  								declaration = originalDeclaration
   862  							}
   863  						}
   864  						break
   865  					}
   866  				}
   867  			}
   868  		}
   869  
   870  		// Special formatting cases.
   871  		switch typ := obj.Type().(type) {
   872  		case *types.Named:
   873  			// Try to add a formatted duration as an inline comment.
   874  			pkg := typ.Obj().Pkg()
   875  			if pkg.Path() == "time" && typ.Obj().Name() == "Duration" {
   876  				if d, ok := constant.Int64Val(obj.Val()); ok {
   877  					comment = time.Duration(d).String()
   878  				}
   879  			}
   880  		}
   881  		if comment == declaration {
   882  			comment = ""
   883  		}
   884  
   885  		str += " = " + declaration
   886  		if comment != "" {
   887  			str += " // " + comment
   888  		}
   889  	}
   890  	return str
   891  }
   892  
   893  // HoverDocForObject returns the best doc comment for obj (for which
   894  // fset provides file/line information).
   895  //
   896  // TODO(rfindley): there appears to be zero(!) tests for this functionality.
   897  func HoverDocForObject(ctx context.Context, snapshot *cache.Snapshot, fset *token.FileSet, obj types.Object) (*ast.CommentGroup, error) {
   898  	if _, isTypeName := obj.(*types.TypeName); isTypeName {
   899  		if _, isTypeParam := obj.Type().(*types.TypeParam); isTypeParam {
   900  			return nil, nil
   901  		}
   902  	}
   903  
   904  	pgf, pos, err := parseFull(ctx, snapshot, fset, obj.Pos())
   905  	if err != nil {
   906  		return nil, fmt.Errorf("re-parsing: %v", err)
   907  	}
   908  
   909  	decl, spec, field := findDeclInfo([]*ast.File{pgf.File}, pos)
   910  	return chooseDocComment(decl, spec, field), nil
   911  }
   912  
   913  func chooseDocComment(decl ast.Decl, spec ast.Spec, field *ast.Field) *ast.CommentGroup {
   914  	if field != nil {
   915  		if field.Doc != nil {
   916  			return field.Doc
   917  		}
   918  		if field.Comment != nil {
   919  			return field.Comment
   920  		}
   921  		return nil
   922  	}
   923  	switch decl := decl.(type) {
   924  	case *ast.FuncDecl:
   925  		return decl.Doc
   926  	case *ast.GenDecl:
   927  		switch spec := spec.(type) {
   928  		case *ast.ValueSpec:
   929  			if spec.Doc != nil {
   930  				return spec.Doc
   931  			}
   932  			if decl.Doc != nil {
   933  				return decl.Doc
   934  			}
   935  			return spec.Comment
   936  		case *ast.TypeSpec:
   937  			if spec.Doc != nil {
   938  				return spec.Doc
   939  			}
   940  			if decl.Doc != nil {
   941  				return decl.Doc
   942  			}
   943  			return spec.Comment
   944  		}
   945  	}
   946  	return nil
   947  }
   948  
   949  // parseFull fully parses the file corresponding to position pos (for
   950  // which fset provides file/line information).
   951  //
   952  // It returns the resulting parsego.File as well as new pos contained
   953  // in the parsed file.
   954  //
   955  // BEWARE: the provided FileSet is used only to interpret the provided
   956  // pos; the resulting File and Pos may belong to the same or a
   957  // different FileSet, such as one synthesized by the parser cache, if
   958  // parse-caching is enabled.
   959  func parseFull(ctx context.Context, snapshot *cache.Snapshot, fset *token.FileSet, pos token.Pos) (*parsego.File, token.Pos, error) {
   960  	f := fset.File(pos)
   961  	if f == nil {
   962  		return nil, 0, bug.Errorf("internal error: no file for position %d", pos)
   963  	}
   964  
   965  	uri := protocol.URIFromPath(f.Name())
   966  	fh, err := snapshot.ReadFile(ctx, uri)
   967  	if err != nil {
   968  		return nil, 0, err
   969  	}
   970  
   971  	pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
   972  	if err != nil {
   973  		return nil, 0, err
   974  	}
   975  
   976  	offset, err := safetoken.Offset(f, pos)
   977  	if err != nil {
   978  		return nil, 0, bug.Errorf("offset out of bounds in %q", uri)
   979  	}
   980  
   981  	fullPos, err := safetoken.Pos(pgf.Tok, offset)
   982  	if err != nil {
   983  		return nil, 0, err
   984  	}
   985  
   986  	return pgf, fullPos, nil
   987  }
   988  
   989  func formatHover(h *hoverJSON, options *settings.Options) (string, error) {
   990  	maybeMarkdown := func(s string) string {
   991  		if s != "" && options.PreferredContentFormat == protocol.Markdown {
   992  			s = fmt.Sprintf("```go\n%s\n```", strings.Trim(s, "\n"))
   993  		}
   994  		return s
   995  	}
   996  
   997  	switch options.HoverKind {
   998  	case settings.SingleLine:
   999  		return h.SingleLine, nil
  1000  
  1001  	case settings.NoDocumentation:
  1002  		return maybeMarkdown(h.Signature), nil
  1003  
  1004  	case settings.Structured:
  1005  		b, err := json.Marshal(h)
  1006  		if err != nil {
  1007  			return "", err
  1008  		}
  1009  		return string(b), nil
  1010  
  1011  	case settings.SynopsisDocumentation,
  1012  		settings.FullDocumentation:
  1013  		// For types, we display TypeDecl and Methods,
  1014  		// but not Signature, which is redundant (= TypeDecl + "\n" + Methods).
  1015  		// For all other symbols, we display Signature;
  1016  		// TypeDecl and Methods are empty.
  1017  		// (This awkwardness is to preserve JSON compatibility.)
  1018  		parts := []string{
  1019  			maybeMarkdown(h.Signature),
  1020  			maybeMarkdown(h.typeDecl),
  1021  			formatDoc(h, options),
  1022  			maybeMarkdown(h.promotedFields),
  1023  			maybeMarkdown(h.methods),
  1024  			formatLink(h, options),
  1025  		}
  1026  		if h.typeDecl != "" {
  1027  			parts[0] = "" // type: suppress redundant Signature
  1028  		}
  1029  		parts = slices.Remove(parts, "")
  1030  
  1031  		var b strings.Builder
  1032  		for i, part := range parts {
  1033  			if i > 0 {
  1034  				if options.PreferredContentFormat == protocol.Markdown {
  1035  					b.WriteString("\n\n")
  1036  				} else {
  1037  					b.WriteByte('\n')
  1038  				}
  1039  			}
  1040  			b.WriteString(part)
  1041  		}
  1042  		return b.String(), nil
  1043  
  1044  	default:
  1045  		return "", fmt.Errorf("invalid HoverKind: %v", options.HoverKind)
  1046  	}
  1047  }
  1048  
  1049  func formatLink(h *hoverJSON, options *settings.Options) string {
  1050  	if !options.LinksInHover || options.LinkTarget == "" || h.LinkPath == "" {
  1051  		return ""
  1052  	}
  1053  	plainLink := cache.BuildLink(options.LinkTarget, h.LinkPath, h.LinkAnchor)
  1054  	switch options.PreferredContentFormat {
  1055  	case protocol.Markdown:
  1056  		return fmt.Sprintf("[`%s` on %s](%s)", h.SymbolName, options.LinkTarget, plainLink)
  1057  	case protocol.PlainText:
  1058  		return ""
  1059  	default:
  1060  		return plainLink
  1061  	}
  1062  }
  1063  
  1064  func formatDoc(h *hoverJSON, options *settings.Options) string {
  1065  	var doc string
  1066  	switch options.HoverKind {
  1067  	case settings.SynopsisDocumentation:
  1068  		doc = h.Synopsis
  1069  	case settings.FullDocumentation:
  1070  		doc = h.FullDocumentation
  1071  	}
  1072  	if options.PreferredContentFormat == protocol.Markdown {
  1073  		return CommentToMarkdown(doc, options)
  1074  	}
  1075  	return doc
  1076  }
  1077  
  1078  // findDeclInfo returns the syntax nodes involved in the declaration of the
  1079  // types.Object with position pos, searching the given list of file syntax
  1080  // trees.
  1081  //
  1082  // Pos may be the position of the name-defining identifier in a FuncDecl,
  1083  // ValueSpec, TypeSpec, Field, or as a special case the position of
  1084  // Ellipsis.Elt in an ellipsis field.
  1085  //
  1086  // If found, the resulting decl, spec, and field will be the inner-most
  1087  // instance of each node type surrounding pos.
  1088  //
  1089  // If field is non-nil, pos is the position of a field Var. If field is nil and
  1090  // spec is non-nil, pos is the position of a Var, Const, or TypeName object. If
  1091  // both field and spec are nil and decl is non-nil, pos is the position of a
  1092  // Func object.
  1093  //
  1094  // It returns a nil decl if no object-defining node is found at pos.
  1095  //
  1096  // TODO(rfindley): this function has tricky semantics, and may be worth unit
  1097  // testing and/or refactoring.
  1098  func findDeclInfo(files []*ast.File, pos token.Pos) (decl ast.Decl, spec ast.Spec, field *ast.Field) {
  1099  	// panic(found{}) breaks off the traversal and
  1100  	// causes the function to return normally.
  1101  	type found struct{}
  1102  	defer func() {
  1103  		switch x := recover().(type) {
  1104  		case nil:
  1105  		case found:
  1106  		default:
  1107  			panic(x)
  1108  		}
  1109  	}()
  1110  
  1111  	// Visit the files in search of the node at pos.
  1112  	stack := make([]ast.Node, 0, 20)
  1113  	// Allocate the closure once, outside the loop.
  1114  	f := func(n ast.Node) bool {
  1115  		if n != nil {
  1116  			stack = append(stack, n) // push
  1117  		} else {
  1118  			stack = stack[:len(stack)-1] // pop
  1119  			return false
  1120  		}
  1121  
  1122  		// Skip subtrees (incl. files) that don't contain the search point.
  1123  		if !(n.Pos() <= pos && pos < n.End()) {
  1124  			return false
  1125  		}
  1126  
  1127  		switch n := n.(type) {
  1128  		case *ast.Field:
  1129  			findEnclosingDeclAndSpec := func() {
  1130  				for i := len(stack) - 1; i >= 0; i-- {
  1131  					switch n := stack[i].(type) {
  1132  					case ast.Spec:
  1133  						spec = n
  1134  					case ast.Decl:
  1135  						decl = n
  1136  						return
  1137  					}
  1138  				}
  1139  			}
  1140  
  1141  			// Check each field name since you can have
  1142  			// multiple names for the same type expression.
  1143  			for _, id := range n.Names {
  1144  				if id.Pos() == pos {
  1145  					field = n
  1146  					findEnclosingDeclAndSpec()
  1147  					panic(found{})
  1148  				}
  1149  			}
  1150  
  1151  			// Check *ast.Field itself. This handles embedded
  1152  			// fields which have no associated *ast.Ident name.
  1153  			if n.Pos() == pos {
  1154  				field = n
  1155  				findEnclosingDeclAndSpec()
  1156  				panic(found{})
  1157  			}
  1158  
  1159  			// Also check "X" in "...X". This makes it easy to format variadic
  1160  			// signature params properly.
  1161  			//
  1162  			// TODO(rfindley): I don't understand this comment. How does finding the
  1163  			// field in this case make it easier to format variadic signature params?
  1164  			if ell, ok := n.Type.(*ast.Ellipsis); ok && ell.Elt != nil && ell.Elt.Pos() == pos {
  1165  				field = n
  1166  				findEnclosingDeclAndSpec()
  1167  				panic(found{})
  1168  			}
  1169  
  1170  		case *ast.FuncDecl:
  1171  			if n.Name.Pos() == pos {
  1172  				decl = n
  1173  				panic(found{})
  1174  			}
  1175  
  1176  		case *ast.GenDecl:
  1177  			for _, s := range n.Specs {
  1178  				switch s := s.(type) {
  1179  				case *ast.TypeSpec:
  1180  					if s.Name.Pos() == pos {
  1181  						decl = n
  1182  						spec = s
  1183  						panic(found{})
  1184  					}
  1185  				case *ast.ValueSpec:
  1186  					for _, id := range s.Names {
  1187  						if id.Pos() == pos {
  1188  							decl = n
  1189  							spec = s
  1190  							panic(found{})
  1191  						}
  1192  					}
  1193  				}
  1194  			}
  1195  		}
  1196  		return true
  1197  	}
  1198  	for _, file := range files {
  1199  		ast.Inspect(file, f)
  1200  	}
  1201  
  1202  	return nil, nil, nil
  1203  }
  1204  
  1205  type promotedField struct {
  1206  	path  string // path (e.g. "x.y" through embedded fields)
  1207  	field *types.Var
  1208  }
  1209  
  1210  // promotedFields returns the list of accessible promoted fields of a struct type t.
  1211  // (Logic plundered from x/tools/cmd/guru/describe.go.)
  1212  func promotedFields(t types.Type, from *types.Package) []promotedField {
  1213  	wantField := func(f *types.Var) bool {
  1214  		if !accessibleTo(f, from) {
  1215  			return false
  1216  		}
  1217  		// Check that the field is not shadowed.
  1218  		obj, _, _ := types.LookupFieldOrMethod(t, true, f.Pkg(), f.Name())
  1219  		return obj == f
  1220  	}
  1221  
  1222  	var fields []promotedField
  1223  	var visit func(t types.Type, stack []*types.Named)
  1224  	visit = func(t types.Type, stack []*types.Named) {
  1225  		tStruct, ok := Deref(t).Underlying().(*types.Struct)
  1226  		if !ok {
  1227  			return
  1228  		}
  1229  	fieldloop:
  1230  		for i := 0; i < tStruct.NumFields(); i++ {
  1231  			f := tStruct.Field(i)
  1232  
  1233  			// Handle recursion through anonymous fields.
  1234  			if f.Anonymous() {
  1235  				tf := f.Type()
  1236  				if ptr, ok := tf.(*types.Pointer); ok {
  1237  					tf = ptr.Elem()
  1238  				}
  1239  				if named, ok := tf.(*types.Named); ok { // (be defensive)
  1240  					// If we've already visited this named type
  1241  					// on this path, break the cycle.
  1242  					for _, x := range stack {
  1243  						if x.Origin() == named.Origin() {
  1244  							continue fieldloop
  1245  						}
  1246  					}
  1247  					visit(f.Type(), append(stack, named))
  1248  				}
  1249  			}
  1250  
  1251  			// Save accessible promoted fields.
  1252  			if len(stack) > 0 && wantField(f) {
  1253  				var path strings.Builder
  1254  				for i, t := range stack {
  1255  					if i > 0 {
  1256  						path.WriteByte('.')
  1257  					}
  1258  					path.WriteString(t.Obj().Name())
  1259  				}
  1260  				fields = append(fields, promotedField{
  1261  					path:  path.String(),
  1262  					field: f,
  1263  				})
  1264  			}
  1265  		}
  1266  	}
  1267  	visit(t, nil)
  1268  
  1269  	return fields
  1270  }
  1271  
  1272  func accessibleTo(obj types.Object, pkg *types.Package) bool {
  1273  	return obj.Exported() || obj.Pkg() == pkg
  1274  }