golang.org/x/tools/gopls@v0.15.3/internal/golang/references.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  // This file defines the 'references' query based on a serializable
     8  // index constructed during type checking, thus avoiding the need to
     9  // type-check packages at search time.
    10  //
    11  // See the ./xrefs/ subpackage for the index construction and lookup.
    12  //
    13  // This implementation does not intermingle objects from distinct
    14  // calls to TypeCheck.
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"go/ast"
    20  	"go/token"
    21  	"go/types"
    22  	"sort"
    23  	"strings"
    24  	"sync"
    25  
    26  	"golang.org/x/sync/errgroup"
    27  	"golang.org/x/tools/go/types/objectpath"
    28  	"golang.org/x/tools/gopls/internal/cache"
    29  	"golang.org/x/tools/gopls/internal/cache/metadata"
    30  	"golang.org/x/tools/gopls/internal/cache/methodsets"
    31  	"golang.org/x/tools/gopls/internal/file"
    32  	"golang.org/x/tools/gopls/internal/protocol"
    33  	"golang.org/x/tools/gopls/internal/util/bug"
    34  	"golang.org/x/tools/gopls/internal/util/safetoken"
    35  	"golang.org/x/tools/internal/event"
    36  )
    37  
    38  // References returns a list of all references (sorted with
    39  // definitions before uses) to the object denoted by the identifier at
    40  // the given file/position, searching the entire workspace.
    41  func References(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp protocol.Position, includeDeclaration bool) ([]protocol.Location, error) {
    42  	references, err := references(ctx, snapshot, fh, pp, includeDeclaration)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	locations := make([]protocol.Location, len(references))
    47  	for i, ref := range references {
    48  		locations[i] = ref.location
    49  	}
    50  	return locations, nil
    51  }
    52  
    53  // A reference describes an identifier that refers to the same
    54  // object as the subject of a References query.
    55  type reference struct {
    56  	isDeclaration bool
    57  	location      protocol.Location
    58  	pkgPath       PackagePath // of declaring package (same for all elements of the slice)
    59  }
    60  
    61  // references returns a list of all references (sorted with
    62  // definitions before uses) to the object denoted by the identifier at
    63  // the given file/position, searching the entire workspace.
    64  func references(ctx context.Context, snapshot *cache.Snapshot, f file.Handle, pp protocol.Position, includeDeclaration bool) ([]reference, error) {
    65  	ctx, done := event.Start(ctx, "golang.references")
    66  	defer done()
    67  
    68  	// Is the cursor within the package name declaration?
    69  	_, inPackageName, err := parsePackageNameDecl(ctx, snapshot, f, pp)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	var refs []reference
    75  	if inPackageName {
    76  		refs, err = packageReferences(ctx, snapshot, f.URI())
    77  	} else {
    78  		refs, err = ordinaryReferences(ctx, snapshot, f.URI(), pp)
    79  	}
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	sort.Slice(refs, func(i, j int) bool {
    85  		x, y := refs[i], refs[j]
    86  		if x.isDeclaration != y.isDeclaration {
    87  			return x.isDeclaration // decls < refs
    88  		}
    89  		return protocol.CompareLocation(x.location, y.location) < 0
    90  	})
    91  
    92  	// De-duplicate by location, and optionally remove declarations.
    93  	out := refs[:0]
    94  	for _, ref := range refs {
    95  		if !includeDeclaration && ref.isDeclaration {
    96  			continue
    97  		}
    98  		if len(out) == 0 || out[len(out)-1].location != ref.location {
    99  			out = append(out, ref)
   100  		}
   101  	}
   102  	refs = out
   103  
   104  	return refs, nil
   105  }
   106  
   107  // packageReferences returns a list of references to the package
   108  // declaration of the specified name and uri by searching among the
   109  // import declarations of all packages that directly import the target
   110  // package.
   111  func packageReferences(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI) ([]reference, error) {
   112  	metas, err := snapshot.MetadataForFile(ctx, uri)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	if len(metas) == 0 {
   117  		return nil, fmt.Errorf("found no package containing %s", uri)
   118  	}
   119  
   120  	var refs []reference
   121  
   122  	// Find external references to the package declaration
   123  	// from each direct import of the package.
   124  	//
   125  	// The narrowest package is the most broadly imported,
   126  	// so we choose it for the external references.
   127  	//
   128  	// But if the file ends with _test.go then we need to
   129  	// find the package it is testing; there's no direct way
   130  	// to do that, so pick a file from the same package that
   131  	// doesn't end in _test.go and start over.
   132  	narrowest := metas[0]
   133  	if narrowest.ForTest != "" && strings.HasSuffix(string(uri), "_test.go") {
   134  		for _, f := range narrowest.CompiledGoFiles {
   135  			if !strings.HasSuffix(string(f), "_test.go") {
   136  				return packageReferences(ctx, snapshot, f)
   137  			}
   138  		}
   139  		// This package has no non-test files.
   140  		// Skip the search for external references.
   141  		// (Conceivably one could blank-import an empty package, but why?)
   142  	} else {
   143  		rdeps, err := snapshot.ReverseDependencies(ctx, narrowest.ID, false) // direct
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  
   148  		// Restrict search to workspace packages.
   149  		workspace, err := snapshot.WorkspaceMetadata(ctx)
   150  		if err != nil {
   151  			return nil, err
   152  		}
   153  		workspaceMap := make(map[PackageID]*metadata.Package, len(workspace))
   154  		for _, mp := range workspace {
   155  			workspaceMap[mp.ID] = mp
   156  		}
   157  
   158  		for _, rdep := range rdeps {
   159  			if _, ok := workspaceMap[rdep.ID]; !ok {
   160  				continue
   161  			}
   162  			for _, uri := range rdep.CompiledGoFiles {
   163  				fh, err := snapshot.ReadFile(ctx, uri)
   164  				if err != nil {
   165  					return nil, err
   166  				}
   167  				f, err := snapshot.ParseGo(ctx, fh, ParseHeader)
   168  				if err != nil {
   169  					return nil, err
   170  				}
   171  				for _, imp := range f.File.Imports {
   172  					if rdep.DepsByImpPath[metadata.UnquoteImportPath(imp)] == narrowest.ID {
   173  						refs = append(refs, reference{
   174  							isDeclaration: false,
   175  							location:      mustLocation(f, imp),
   176  							pkgPath:       narrowest.PkgPath,
   177  						})
   178  					}
   179  				}
   180  			}
   181  		}
   182  	}
   183  
   184  	// Find internal "references" to the package from
   185  	// of each package declaration in the target package itself.
   186  	//
   187  	// The widest package (possibly a test variant) has the
   188  	// greatest number of files and thus we choose it for the
   189  	// "internal" references.
   190  	widest := metas[len(metas)-1] // may include _test.go files
   191  	for _, uri := range widest.CompiledGoFiles {
   192  		fh, err := snapshot.ReadFile(ctx, uri)
   193  		if err != nil {
   194  			return nil, err
   195  		}
   196  		f, err := snapshot.ParseGo(ctx, fh, ParseHeader)
   197  		if err != nil {
   198  			return nil, err
   199  		}
   200  		// golang/go#66250: don't crash if the package file lacks a name.
   201  		if f.File.Name.Pos().IsValid() {
   202  			refs = append(refs, reference{
   203  				isDeclaration: true, // (one of many)
   204  				location:      mustLocation(f, f.File.Name),
   205  				pkgPath:       widest.PkgPath,
   206  			})
   207  		}
   208  	}
   209  
   210  	return refs, nil
   211  }
   212  
   213  // ordinaryReferences computes references for all ordinary objects (not package declarations).
   214  func ordinaryReferences(ctx context.Context, snapshot *cache.Snapshot, uri protocol.DocumentURI, pp protocol.Position) ([]reference, error) {
   215  	// Strategy: use the reference information computed by the
   216  	// type checker to find the declaration. First type-check this
   217  	// package to find the declaration, then type check the
   218  	// declaring package (which may be different), plus variants,
   219  	// to find local (in-package) references.
   220  	// Global references are satisfied by the index.
   221  
   222  	// Strictly speaking, a wider package could provide a different
   223  	// declaration (e.g. because the _test.go files can change the
   224  	// meaning of a field or method selection), but the narrower
   225  	// package reports the more broadly referenced object.
   226  	pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, uri)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	// Find the selected object (declaration or reference).
   232  	// For struct{T}, we choose the field (Def) over the type (Use).
   233  	pos, err := pgf.PositionPos(pp)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	candidates, _, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	// Pick first object arbitrarily.
   243  	// The case variables of a type switch have different
   244  	// types but that difference is immaterial here.
   245  	var obj types.Object
   246  	for obj = range candidates {
   247  		break
   248  	}
   249  	if obj == nil {
   250  		return nil, ErrNoIdentFound // can't happen
   251  	}
   252  
   253  	// nil, error, error.Error, iota, or other built-in?
   254  	if obj.Pkg() == nil {
   255  		return nil, fmt.Errorf("references to builtin %q are not supported", obj.Name())
   256  	}
   257  	if !obj.Pos().IsValid() {
   258  		if obj.Pkg().Path() != "unsafe" {
   259  			bug.Reportf("references: object %v has no position", obj)
   260  		}
   261  		return nil, fmt.Errorf("references to unsafe.%s are not supported", obj.Name())
   262  	}
   263  
   264  	// Find metadata of all packages containing the object's defining file.
   265  	// This may include the query pkg, and possibly other variants.
   266  	declPosn := safetoken.StartPosition(pkg.FileSet(), obj.Pos())
   267  	declURI := protocol.URIFromPath(declPosn.Filename)
   268  	variants, err := snapshot.MetadataForFile(ctx, declURI)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  	if len(variants) == 0 {
   273  		return nil, fmt.Errorf("no packages for file %q", declURI) // can't happen
   274  	}
   275  	// (variants must include ITVs for reverse dependency computation below.)
   276  
   277  	// Is object exported?
   278  	// If so, compute scope and targets of the global search.
   279  	var (
   280  		globalScope   = make(map[PackageID]*metadata.Package) // (excludes ITVs)
   281  		globalTargets map[PackagePath]map[objectpath.Path]unit
   282  		expansions    = make(map[PackageID]unit) // packages that caused search expansion
   283  	)
   284  	// TODO(adonovan): what about generic functions? Need to consider both
   285  	// uninstantiated and instantiated. The latter have no objectpath. Use Origin?
   286  	if path, err := objectpath.For(obj); err == nil && obj.Exported() {
   287  		pkgPath := variants[0].PkgPath // (all variants have same package path)
   288  		globalTargets = map[PackagePath]map[objectpath.Path]unit{
   289  			pkgPath: {path: {}}, // primary target
   290  		}
   291  
   292  		// Compute set of (non-ITV) workspace packages.
   293  		// We restrict references to this subset.
   294  		workspace, err := snapshot.WorkspaceMetadata(ctx)
   295  		if err != nil {
   296  			return nil, err
   297  		}
   298  		workspaceMap := make(map[PackageID]*metadata.Package, len(workspace))
   299  		workspaceIDs := make([]PackageID, 0, len(workspace))
   300  		for _, mp := range workspace {
   301  			workspaceMap[mp.ID] = mp
   302  			workspaceIDs = append(workspaceIDs, mp.ID)
   303  		}
   304  
   305  		// addRdeps expands the global scope to include the
   306  		// reverse dependencies of the specified package.
   307  		addRdeps := func(id PackageID, transitive bool) error {
   308  			rdeps, err := snapshot.ReverseDependencies(ctx, id, transitive)
   309  			if err != nil {
   310  				return err
   311  			}
   312  			for rdepID, rdep := range rdeps {
   313  				// Skip non-workspace packages.
   314  				//
   315  				// This means we also skip any expansion of the
   316  				// search that might be caused by a non-workspace
   317  				// package, possibly causing us to miss references
   318  				// to the expanded target set from workspace packages.
   319  				//
   320  				// TODO(adonovan): don't skip those expansions.
   321  				// The challenge is how to so without type-checking
   322  				// a lot of non-workspace packages not covered by
   323  				// the initial workspace load.
   324  				if _, ok := workspaceMap[rdepID]; !ok {
   325  					continue
   326  				}
   327  
   328  				globalScope[rdepID] = rdep
   329  			}
   330  			return nil
   331  		}
   332  
   333  		// How far need we search?
   334  		// For package-level objects, we need only search the direct importers.
   335  		// For fields and methods, we must search transitively.
   336  		transitive := obj.Pkg().Scope().Lookup(obj.Name()) != obj
   337  
   338  		// The scope is the union of rdeps of each variant.
   339  		// (Each set is disjoint so there's no benefit to
   340  		// combining the metadata graph traversals.)
   341  		for _, mp := range variants {
   342  			if err := addRdeps(mp.ID, transitive); err != nil {
   343  				return nil, err
   344  			}
   345  		}
   346  
   347  		// Is object a method?
   348  		//
   349  		// If so, expand the search so that the targets include
   350  		// all methods that correspond to it through interface
   351  		// satisfaction, and the scope includes the rdeps of
   352  		// the package that declares each corresponding type.
   353  		//
   354  		// 'expansions' records the packages that declared
   355  		// such types.
   356  		if recv := effectiveReceiver(obj); recv != nil {
   357  			if err := expandMethodSearch(ctx, snapshot, workspaceIDs, obj.(*types.Func), recv, addRdeps, globalTargets, expansions); err != nil {
   358  				return nil, err
   359  			}
   360  		}
   361  	}
   362  
   363  	// The search functions will call report(loc) for each hit.
   364  	var (
   365  		refsMu sync.Mutex
   366  		refs   []reference
   367  	)
   368  	report := func(loc protocol.Location, isDecl bool) {
   369  		ref := reference{
   370  			isDeclaration: isDecl,
   371  			location:      loc,
   372  			pkgPath:       pkg.Metadata().PkgPath,
   373  		}
   374  		refsMu.Lock()
   375  		refs = append(refs, ref)
   376  		refsMu.Unlock()
   377  	}
   378  
   379  	// Loop over the variants of the declaring package,
   380  	// and perform both the local (in-package) and global
   381  	// (cross-package) searches, in parallel.
   382  	//
   383  	// TODO(adonovan): opt: support LSP reference streaming. See:
   384  	// - https://github.com/microsoft/vscode-languageserver-node/pull/164
   385  	// - https://github.com/microsoft/language-server-protocol/pull/182
   386  	//
   387  	// Careful: this goroutine must not return before group.Wait.
   388  	var group errgroup.Group
   389  
   390  	// Compute local references for each variant.
   391  	// The target objects are identified by (URI, offset).
   392  	for _, mp := range variants {
   393  		// We want the ordinary importable package,
   394  		// plus any test-augmented variants, since
   395  		// declarations in _test.go files may change
   396  		// the reference of a selection, or even a
   397  		// field into a method or vice versa.
   398  		//
   399  		// But we don't need intermediate test variants,
   400  		// as their local references will be covered
   401  		// already by other variants.
   402  		if mp.IsIntermediateTestVariant() {
   403  			continue
   404  		}
   405  		mp := mp
   406  		group.Go(func() error {
   407  			// TODO(adonovan): opt: batch these TypeChecks.
   408  			pkgs, err := snapshot.TypeCheck(ctx, mp.ID)
   409  			if err != nil {
   410  				return err
   411  			}
   412  			pkg := pkgs[0]
   413  
   414  			// Find the declaration of the corresponding
   415  			// object in this package based on (URI, offset).
   416  			pgf, err := pkg.File(declURI)
   417  			if err != nil {
   418  				return err
   419  			}
   420  			pos, err := safetoken.Pos(pgf.Tok, declPosn.Offset)
   421  			if err != nil {
   422  				return err
   423  			}
   424  			objects, _, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos)
   425  			if err != nil {
   426  				return err // unreachable? (probably caught earlier)
   427  			}
   428  
   429  			// Report the locations of the declaration(s).
   430  			// TODO(adonovan): what about for corresponding methods? Add tests.
   431  			for _, node := range objects {
   432  				report(mustLocation(pgf, node), true)
   433  			}
   434  
   435  			// Convert targets map to set.
   436  			targets := make(map[types.Object]bool)
   437  			for obj := range objects {
   438  				targets[obj] = true
   439  			}
   440  
   441  			return localReferences(pkg, targets, true, report)
   442  		})
   443  	}
   444  
   445  	// Also compute local references within packages that declare
   446  	// corresponding methods (see above), which expand the global search.
   447  	// The target objects are identified by (PkgPath, objectpath).
   448  	for id := range expansions {
   449  		id := id
   450  		group.Go(func() error {
   451  			// TODO(adonovan): opt: batch these TypeChecks.
   452  			pkgs, err := snapshot.TypeCheck(ctx, id)
   453  			if err != nil {
   454  				return err
   455  			}
   456  			pkg := pkgs[0]
   457  
   458  			targets := make(map[types.Object]bool)
   459  			for objpath := range globalTargets[pkg.Metadata().PkgPath] {
   460  				obj, err := objectpath.Object(pkg.GetTypes(), objpath)
   461  				if err != nil {
   462  					// No such object, because it was
   463  					// declared only in the test variant.
   464  					continue
   465  				}
   466  				targets[obj] = true
   467  			}
   468  
   469  			// Don't include corresponding types or methods
   470  			// since expansions did that already, and we don't
   471  			// want (e.g.) concrete -> interface -> concrete.
   472  			const correspond = false
   473  			return localReferences(pkg, targets, correspond, report)
   474  		})
   475  	}
   476  
   477  	// Compute global references for selected reverse dependencies.
   478  	group.Go(func() error {
   479  		var globalIDs []PackageID
   480  		for id := range globalScope {
   481  			globalIDs = append(globalIDs, id)
   482  		}
   483  		indexes, err := snapshot.References(ctx, globalIDs...)
   484  		if err != nil {
   485  			return err
   486  		}
   487  		for _, index := range indexes {
   488  			for _, loc := range index.Lookup(globalTargets) {
   489  				report(loc, false)
   490  			}
   491  		}
   492  		return nil
   493  	})
   494  
   495  	if err := group.Wait(); err != nil {
   496  		return nil, err
   497  	}
   498  	return refs, nil
   499  }
   500  
   501  // expandMethodSearch expands the scope and targets of a global search
   502  // for an exported method to include all methods in the workspace
   503  // that correspond to it through interface satisfaction.
   504  //
   505  // Each package that declares a corresponding type is added to
   506  // expansions so that we can also find local references to the type
   507  // within the package, which of course requires type checking.
   508  //
   509  // The scope is expanded by a sequence of calls (not concurrent) to addRdeps.
   510  //
   511  // recv is the method's effective receiver type, for method-set computations.
   512  func expandMethodSearch(ctx context.Context, snapshot *cache.Snapshot, workspaceIDs []PackageID, method *types.Func, recv types.Type, addRdeps func(id PackageID, transitive bool) error, targets map[PackagePath]map[objectpath.Path]unit, expansions map[PackageID]unit) error {
   513  	// Compute the method-set fingerprint used as a key to the global search.
   514  	key, hasMethods := methodsets.KeyOf(recv)
   515  	if !hasMethods {
   516  		return bug.Errorf("KeyOf(%s)={} yet %s is a method", recv, method)
   517  	}
   518  	// Search the methodset index of each package in the workspace.
   519  	indexes, err := snapshot.MethodSets(ctx, workspaceIDs...)
   520  	if err != nil {
   521  		return err
   522  	}
   523  	var mu sync.Mutex // guards addRdeps, targets, expansions
   524  	var group errgroup.Group
   525  	for i, index := range indexes {
   526  		i := i
   527  		index := index
   528  		group.Go(func() error {
   529  			// Consult index for matching methods.
   530  			results := index.Search(key, method.Name())
   531  			if len(results) == 0 {
   532  				return nil
   533  			}
   534  
   535  			// We have discovered one or more corresponding types.
   536  			id := workspaceIDs[i]
   537  
   538  			mu.Lock()
   539  			defer mu.Unlock()
   540  
   541  			// Expand global search scope to include rdeps of this pkg.
   542  			if err := addRdeps(id, true); err != nil {
   543  				return err
   544  			}
   545  
   546  			// Mark this package so that we search within it for
   547  			// local references to the additional types/methods.
   548  			expansions[id] = unit{}
   549  
   550  			// Add each corresponding method the to set of global search targets.
   551  			for _, res := range results {
   552  				methodPkg := PackagePath(res.PkgPath)
   553  				opaths, ok := targets[methodPkg]
   554  				if !ok {
   555  					opaths = make(map[objectpath.Path]unit)
   556  					targets[methodPkg] = opaths
   557  				}
   558  				opaths[res.ObjectPath] = unit{}
   559  			}
   560  			return nil
   561  		})
   562  	}
   563  	return group.Wait()
   564  }
   565  
   566  // localReferences traverses syntax and reports each reference to one
   567  // of the target objects, or (if correspond is set) an object that
   568  // corresponds to one of them via interface satisfaction.
   569  func localReferences(pkg *cache.Package, targets map[types.Object]bool, correspond bool, report func(loc protocol.Location, isDecl bool)) error {
   570  	// If we're searching for references to a method optionally
   571  	// broaden the search to include references to corresponding
   572  	// methods of mutually assignable receiver types.
   573  	// (We use a slice, but objectsAt never returns >1 methods.)
   574  	var methodRecvs []types.Type
   575  	var methodName string // name of an arbitrary target, iff a method
   576  	if correspond {
   577  		for obj := range targets {
   578  			if t := effectiveReceiver(obj); t != nil {
   579  				methodRecvs = append(methodRecvs, t)
   580  				methodName = obj.Name()
   581  			}
   582  		}
   583  	}
   584  
   585  	// matches reports whether obj either is or corresponds to a target.
   586  	// (Correspondence is defined as usual for interface methods.)
   587  	matches := func(obj types.Object) bool {
   588  		if containsOrigin(targets, obj) {
   589  			return true
   590  		}
   591  		if methodRecvs != nil && obj.Name() == methodName {
   592  			if orecv := effectiveReceiver(obj); orecv != nil {
   593  				for _, mrecv := range methodRecvs {
   594  					if concreteImplementsIntf(orecv, mrecv) {
   595  						return true
   596  					}
   597  				}
   598  			}
   599  		}
   600  		return false
   601  	}
   602  
   603  	// Scan through syntax looking for uses of one of the target objects.
   604  	for _, pgf := range pkg.CompiledGoFiles() {
   605  		ast.Inspect(pgf.File, func(n ast.Node) bool {
   606  			if id, ok := n.(*ast.Ident); ok {
   607  				if obj, ok := pkg.GetTypesInfo().Uses[id]; ok && matches(obj) {
   608  					report(mustLocation(pgf, id), false)
   609  				}
   610  			}
   611  			return true
   612  		})
   613  	}
   614  	return nil
   615  }
   616  
   617  // effectiveReceiver returns the effective receiver type for method-set
   618  // comparisons for obj, if it is a method, or nil otherwise.
   619  func effectiveReceiver(obj types.Object) types.Type {
   620  	if fn, ok := obj.(*types.Func); ok {
   621  		if recv := fn.Type().(*types.Signature).Recv(); recv != nil {
   622  			return methodsets.EnsurePointer(recv.Type())
   623  		}
   624  	}
   625  	return nil
   626  }
   627  
   628  // objectsAt returns the non-empty set of objects denoted (def or use)
   629  // by the specified position within a file syntax tree, or an error if
   630  // none were found.
   631  //
   632  // The result may contain more than one element because all case
   633  // variables of a type switch appear to be declared at the same
   634  // position.
   635  //
   636  // Each object is mapped to the syntax node that was treated as an
   637  // identifier, which is not always an ast.Ident. The second component
   638  // of the result is the innermost node enclosing pos.
   639  //
   640  // TODO(adonovan): factor in common with referencedObject.
   641  func objectsAt(info *types.Info, file *ast.File, pos token.Pos) (map[types.Object]ast.Node, ast.Node, error) {
   642  	path := pathEnclosingObjNode(file, pos)
   643  	if path == nil {
   644  		return nil, nil, ErrNoIdentFound
   645  	}
   646  
   647  	targets := make(map[types.Object]ast.Node)
   648  
   649  	switch leaf := path[0].(type) {
   650  	case *ast.Ident:
   651  		// If leaf represents an implicit type switch object or the type
   652  		// switch "assign" variable, expand to all of the type switch's
   653  		// implicit objects.
   654  		if implicits, _ := typeSwitchImplicits(info, path); len(implicits) > 0 {
   655  			for _, obj := range implicits {
   656  				targets[obj] = leaf
   657  			}
   658  		} else {
   659  			// Note: prior to go1.21, go/types issue #60372 causes the position
   660  			// a field Var T created for struct{*p.T} to be recorded at the
   661  			// start of the field type ("*") not the location of the T.
   662  			// This affects references and other gopls operations (issue #60369).
   663  			// TODO(adonovan): delete this comment when we drop support for go1.20.
   664  
   665  			// For struct{T}, we prefer the defined field Var over the used TypeName.
   666  			obj := info.ObjectOf(leaf)
   667  			if obj == nil {
   668  				return nil, nil, fmt.Errorf("%w for %q", errNoObjectFound, leaf.Name)
   669  			}
   670  			targets[obj] = leaf
   671  		}
   672  	case *ast.ImportSpec:
   673  		// Look up the implicit *types.PkgName.
   674  		obj := info.Implicits[leaf]
   675  		if obj == nil {
   676  			return nil, nil, fmt.Errorf("%w for import %s", errNoObjectFound, metadata.UnquoteImportPath(leaf))
   677  		}
   678  		targets[obj] = leaf
   679  	}
   680  
   681  	if len(targets) == 0 {
   682  		return nil, nil, fmt.Errorf("objectAt: internal error: no targets") // can't happen
   683  	}
   684  	return targets, path[0], nil
   685  }
   686  
   687  // mustLocation reports the location interval a syntax node,
   688  // which must belong to m.File.
   689  //
   690  // Safe for use only by references and implementations.
   691  func mustLocation(pgf *ParsedGoFile, n ast.Node) protocol.Location {
   692  	loc, err := pgf.NodeLocation(n)
   693  	if err != nil {
   694  		panic(err) // can't happen in references or implementations
   695  	}
   696  	return loc
   697  }