github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/cache/snapshot.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 cache
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"fmt"
    11  	"go/ast"
    12  	"go/token"
    13  	"go/types"
    14  	"io"
    15  	"io/ioutil"
    16  	"os"
    17  	"path/filepath"
    18  	"regexp"
    19  	"sort"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  
    24  	"golang.org/x/mod/modfile"
    25  	"golang.org/x/mod/module"
    26  	"golang.org/x/mod/semver"
    27  	"github.com/powerman/golang-tools/go/analysis"
    28  	"github.com/powerman/golang-tools/go/packages"
    29  	"github.com/powerman/golang-tools/internal/event"
    30  	"github.com/powerman/golang-tools/internal/gocommand"
    31  	"github.com/powerman/golang-tools/internal/lsp/debug/log"
    32  	"github.com/powerman/golang-tools/internal/lsp/debug/tag"
    33  	"github.com/powerman/golang-tools/internal/lsp/source"
    34  	"github.com/powerman/golang-tools/internal/memoize"
    35  	"github.com/powerman/golang-tools/internal/packagesinternal"
    36  	"github.com/powerman/golang-tools/internal/span"
    37  	"github.com/powerman/golang-tools/internal/typesinternal"
    38  	errors "golang.org/x/xerrors"
    39  )
    40  
    41  type snapshot struct {
    42  	memoize.Arg // allow as a memoize.Function arg
    43  
    44  	id   uint64
    45  	view *View
    46  
    47  	cancel        func()
    48  	backgroundCtx context.Context
    49  
    50  	// the cache generation that contains the data for this snapshot.
    51  	generation *memoize.Generation
    52  
    53  	// The snapshot's initialization state is controlled by the fields below.
    54  	//
    55  	// initializeOnce guards snapshot initialization. Each snapshot is
    56  	// initialized at most once: reinitialization is triggered on later snapshots
    57  	// by invalidating this field.
    58  	initializeOnce *sync.Once
    59  	// initializedErr holds the last error resulting from initialization. If
    60  	// initialization fails, we only retry when the the workspace modules change,
    61  	// to avoid too many go/packages calls.
    62  	initializedErr *source.CriticalError
    63  
    64  	// mu guards all of the maps in the snapshot, as well as the builtin URI.
    65  	mu sync.Mutex
    66  
    67  	// builtin pins the AST and package for builtin.go in memory.
    68  	builtin span.URI
    69  
    70  	// ids maps file URIs to package IDs.
    71  	// It may be invalidated on calls to go/packages.
    72  	ids map[span.URI][]PackageID
    73  
    74  	// metadata maps file IDs to their associated metadata.
    75  	// It may invalidated on calls to go/packages.
    76  	metadata map[PackageID]*KnownMetadata
    77  
    78  	// importedBy maps package IDs to the list of packages that import them.
    79  	importedBy map[PackageID][]PackageID
    80  
    81  	// files maps file URIs to their corresponding FileHandles.
    82  	// It may invalidated when a file's content changes.
    83  	files map[span.URI]source.VersionedFileHandle
    84  
    85  	// goFiles maps a parseKey to its parseGoHandle.
    86  	goFiles map[parseKey]*parseGoHandle
    87  
    88  	// TODO(rfindley): consider merging this with files to reduce burden on clone.
    89  	symbols map[span.URI]*symbolHandle
    90  
    91  	// packages maps a packageKey to a set of packageHandles to which that file belongs.
    92  	// It may be invalidated when a file's content changes.
    93  	packages map[packageKey]*packageHandle
    94  
    95  	// actions maps an actionkey to its actionHandle.
    96  	actions map[actionKey]*actionHandle
    97  
    98  	// workspacePackages contains the workspace's packages, which are loaded
    99  	// when the view is created.
   100  	workspacePackages map[PackageID]PackagePath
   101  
   102  	// unloadableFiles keeps track of files that we've failed to load.
   103  	unloadableFiles map[span.URI]struct{}
   104  
   105  	// parseModHandles keeps track of any parseModHandles for the snapshot.
   106  	// The handles need not refer to only the view's go.mod file.
   107  	parseModHandles map[span.URI]*parseModHandle
   108  
   109  	// parseWorkHandles keeps track of any parseWorkHandles for the snapshot.
   110  	// The handles need not refer to only the view's go.work file.
   111  	parseWorkHandles map[span.URI]*parseWorkHandle
   112  
   113  	// Preserve go.mod-related handles to avoid garbage-collecting the results
   114  	// of various calls to the go command. The handles need not refer to only
   115  	// the view's go.mod file.
   116  	modTidyHandles map[span.URI]*modTidyHandle
   117  	modWhyHandles  map[span.URI]*modWhyHandle
   118  
   119  	workspace          *workspace
   120  	workspaceDirHandle *memoize.Handle
   121  
   122  	// knownSubdirs is the set of subdirectories in the workspace, used to
   123  	// create glob patterns for file watching.
   124  	knownSubdirs map[span.URI]struct{}
   125  	// unprocessedSubdirChanges are any changes that might affect the set of
   126  	// subdirectories in the workspace. They are not reflected to knownSubdirs
   127  	// during the snapshot cloning step as it can slow down cloning.
   128  	unprocessedSubdirChanges []*fileChange
   129  }
   130  
   131  type packageKey struct {
   132  	mode source.ParseMode
   133  	id   PackageID
   134  }
   135  
   136  type actionKey struct {
   137  	pkg      packageKey
   138  	analyzer *analysis.Analyzer
   139  }
   140  
   141  func (s *snapshot) ID() uint64 {
   142  	return s.id
   143  }
   144  
   145  func (s *snapshot) View() source.View {
   146  	return s.view
   147  }
   148  
   149  func (s *snapshot) BackgroundContext() context.Context {
   150  	return s.backgroundCtx
   151  }
   152  
   153  func (s *snapshot) FileSet() *token.FileSet {
   154  	return s.view.session.cache.fset
   155  }
   156  
   157  func (s *snapshot) ModFiles() []span.URI {
   158  	var uris []span.URI
   159  	for modURI := range s.workspace.getActiveModFiles() {
   160  		uris = append(uris, modURI)
   161  	}
   162  	return uris
   163  }
   164  
   165  func (s *snapshot) WorkFile() span.URI {
   166  	return s.workspace.workFile
   167  }
   168  
   169  func (s *snapshot) Templates() map[span.URI]source.VersionedFileHandle {
   170  	s.mu.Lock()
   171  	defer s.mu.Unlock()
   172  
   173  	tmpls := map[span.URI]source.VersionedFileHandle{}
   174  	for k, fh := range s.files {
   175  		if s.view.FileKind(fh) == source.Tmpl {
   176  			tmpls[k] = fh
   177  		}
   178  	}
   179  	return tmpls
   180  }
   181  
   182  func (s *snapshot) ValidBuildConfiguration() bool {
   183  	return validBuildConfiguration(s.view.rootURI, &s.view.workspaceInformation, s.workspace.getActiveModFiles())
   184  }
   185  
   186  // workspaceMode describes the way in which the snapshot's workspace should
   187  // be loaded.
   188  func (s *snapshot) workspaceMode() workspaceMode {
   189  	var mode workspaceMode
   190  
   191  	// If the view has an invalid configuration, don't build the workspace
   192  	// module.
   193  	validBuildConfiguration := s.ValidBuildConfiguration()
   194  	if !validBuildConfiguration {
   195  		return mode
   196  	}
   197  	// If the view is not in a module and contains no modules, but still has a
   198  	// valid workspace configuration, do not create the workspace module.
   199  	// It could be using GOPATH or a different build system entirely.
   200  	if len(s.workspace.getActiveModFiles()) == 0 && validBuildConfiguration {
   201  		return mode
   202  	}
   203  	mode |= moduleMode
   204  	options := s.view.Options()
   205  	// The -modfile flag is available for Go versions >= 1.14.
   206  	if options.TempModfile && s.view.workspaceInformation.goversion >= 14 {
   207  		mode |= tempModfile
   208  	}
   209  	return mode
   210  }
   211  
   212  // config returns the configuration used for the snapshot's interaction with
   213  // the go/packages API. It uses the given working directory.
   214  //
   215  // TODO(rstambler): go/packages requires that we do not provide overlays for
   216  // multiple modules in on config, so buildOverlay needs to filter overlays by
   217  // module.
   218  func (s *snapshot) config(ctx context.Context, inv *gocommand.Invocation) *packages.Config {
   219  	s.view.optionsMu.Lock()
   220  	verboseOutput := s.view.options.VerboseOutput
   221  	s.view.optionsMu.Unlock()
   222  
   223  	cfg := &packages.Config{
   224  		Context:    ctx,
   225  		Dir:        inv.WorkingDir,
   226  		Env:        inv.Env,
   227  		BuildFlags: inv.BuildFlags,
   228  		Mode: packages.NeedName |
   229  			packages.NeedFiles |
   230  			packages.NeedCompiledGoFiles |
   231  			packages.NeedImports |
   232  			packages.NeedDeps |
   233  			packages.NeedTypesSizes |
   234  			packages.NeedModule,
   235  		Fset:    s.FileSet(),
   236  		Overlay: s.buildOverlay(),
   237  		ParseFile: func(*token.FileSet, string, []byte) (*ast.File, error) {
   238  			panic("go/packages must not be used to parse files")
   239  		},
   240  		Logf: func(format string, args ...interface{}) {
   241  			if verboseOutput {
   242  				event.Log(ctx, fmt.Sprintf(format, args...))
   243  			}
   244  		},
   245  		Tests: true,
   246  	}
   247  	packagesinternal.SetModFile(cfg, inv.ModFile)
   248  	packagesinternal.SetModFlag(cfg, inv.ModFlag)
   249  	// We want to type check cgo code if go/types supports it.
   250  	if typesinternal.SetUsesCgo(&types.Config{}) {
   251  		cfg.Mode |= packages.LoadMode(packagesinternal.TypecheckCgo)
   252  	}
   253  	packagesinternal.SetGoCmdRunner(cfg, s.view.session.gocmdRunner)
   254  	return cfg
   255  }
   256  
   257  func (s *snapshot) RunGoCommandDirect(ctx context.Context, mode source.InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error) {
   258  	_, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	defer cleanup()
   263  
   264  	return s.view.session.gocmdRunner.Run(ctx, *inv)
   265  }
   266  
   267  func (s *snapshot) RunGoCommandPiped(ctx context.Context, mode source.InvocationFlags, inv *gocommand.Invocation, stdout, stderr io.Writer) error {
   268  	_, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
   269  	if err != nil {
   270  		return err
   271  	}
   272  	defer cleanup()
   273  	return s.view.session.gocmdRunner.RunPiped(ctx, *inv, stdout, stderr)
   274  }
   275  
   276  func (s *snapshot) RunGoCommands(ctx context.Context, allowNetwork bool, wd string, run func(invoke func(...string) (*bytes.Buffer, error)) error) (bool, []byte, []byte, error) {
   277  	var flags source.InvocationFlags
   278  	if s.workspaceMode()&tempModfile != 0 {
   279  		flags = source.WriteTemporaryModFile
   280  	} else {
   281  		flags = source.Normal
   282  	}
   283  	if allowNetwork {
   284  		flags |= source.AllowNetwork
   285  	}
   286  	tmpURI, inv, cleanup, err := s.goCommandInvocation(ctx, flags, &gocommand.Invocation{WorkingDir: wd})
   287  	if err != nil {
   288  		return false, nil, nil, err
   289  	}
   290  	defer cleanup()
   291  	invoke := func(args ...string) (*bytes.Buffer, error) {
   292  		inv.Verb = args[0]
   293  		inv.Args = args[1:]
   294  		return s.view.session.gocmdRunner.Run(ctx, *inv)
   295  	}
   296  	if err := run(invoke); err != nil {
   297  		return false, nil, nil, err
   298  	}
   299  	if flags.Mode() != source.WriteTemporaryModFile {
   300  		return false, nil, nil, nil
   301  	}
   302  	var modBytes, sumBytes []byte
   303  	modBytes, err = ioutil.ReadFile(tmpURI.Filename())
   304  	if err != nil && !os.IsNotExist(err) {
   305  		return false, nil, nil, err
   306  	}
   307  	sumBytes, err = ioutil.ReadFile(strings.TrimSuffix(tmpURI.Filename(), ".mod") + ".sum")
   308  	if err != nil && !os.IsNotExist(err) {
   309  		return false, nil, nil, err
   310  	}
   311  	return true, modBytes, sumBytes, nil
   312  }
   313  
   314  func (s *snapshot) goCommandInvocation(ctx context.Context, flags source.InvocationFlags, inv *gocommand.Invocation) (tmpURI span.URI, updatedInv *gocommand.Invocation, cleanup func(), err error) {
   315  	s.view.optionsMu.Lock()
   316  	allowModfileModificationOption := s.view.options.AllowModfileModifications
   317  	allowNetworkOption := s.view.options.AllowImplicitNetworkAccess
   318  	inv.Env = append(append(append(os.Environ(), s.view.options.EnvSlice()...), inv.Env...), "GO111MODULE="+s.view.effectiveGo111Module)
   319  	inv.BuildFlags = append([]string{}, s.view.options.BuildFlags...)
   320  	s.view.optionsMu.Unlock()
   321  	cleanup = func() {} // fallback
   322  
   323  	// All logic below is for module mode.
   324  	if s.workspaceMode()&moduleMode == 0 {
   325  		return "", inv, cleanup, nil
   326  	}
   327  
   328  	mode, allowNetwork := flags.Mode(), flags.AllowNetwork()
   329  	if !allowNetwork && !allowNetworkOption {
   330  		inv.Env = append(inv.Env, "GOPROXY=off")
   331  	}
   332  
   333  	// What follows is rather complicated logic for how to actually run the go
   334  	// command. A word of warning: this is the result of various incremental
   335  	// features added to gopls, and varying behavior of the Go command across Go
   336  	// versions. It can surely be cleaned up significantly, but tread carefully.
   337  	//
   338  	// Roughly speaking we need to resolve four things:
   339  	//  - the working directory.
   340  	//  - the -mod flag
   341  	//  - the -modfile flag
   342  	//  - the -workfile flag
   343  	//
   344  	// These are dependent on a number of factors: whether we need to run in a
   345  	// synthetic workspace, whether flags are supported at the current go
   346  	// version, and what we're actually trying to achieve (the
   347  	// source.InvocationFlags).
   348  
   349  	var modURI span.URI
   350  	// Select the module context to use.
   351  	// If we're type checking, we need to use the workspace context, meaning
   352  	// the main (workspace) module. Otherwise, we should use the module for
   353  	// the passed-in working dir.
   354  	if mode == source.LoadWorkspace {
   355  		switch s.workspace.moduleSource {
   356  		case legacyWorkspace:
   357  			for m := range s.workspace.getActiveModFiles() { // range to access the only element
   358  				modURI = m
   359  			}
   360  		case goWorkWorkspace:
   361  			if s.view.goversion >= 18 {
   362  				break
   363  			}
   364  			// Before go 1.18, the Go command did not natively support go.work files,
   365  			// so we 'fake' them with a workspace module.
   366  			fallthrough
   367  		case fileSystemWorkspace, goplsModWorkspace:
   368  			var tmpDir span.URI
   369  			var err error
   370  			tmpDir, err = s.getWorkspaceDir(ctx)
   371  			if err != nil {
   372  				return "", nil, cleanup, err
   373  			}
   374  			inv.WorkingDir = tmpDir.Filename()
   375  			modURI = span.URIFromPath(filepath.Join(tmpDir.Filename(), "go.mod"))
   376  		}
   377  	} else {
   378  		modURI = s.GoModForFile(span.URIFromPath(inv.WorkingDir))
   379  	}
   380  
   381  	var modContent []byte
   382  	if modURI != "" {
   383  		modFH, err := s.GetFile(ctx, modURI)
   384  		if err != nil {
   385  			return "", nil, cleanup, err
   386  		}
   387  		modContent, err = modFH.Read()
   388  		if err != nil {
   389  			return "", nil, cleanup, err
   390  		}
   391  	}
   392  
   393  	vendorEnabled, err := s.vendorEnabled(ctx, modURI, modContent)
   394  	if err != nil {
   395  		return "", nil, cleanup, err
   396  	}
   397  
   398  	mutableModFlag := ""
   399  	// If the mod flag isn't set, populate it based on the mode and workspace.
   400  	if inv.ModFlag == "" {
   401  		if s.view.goversion >= 16 {
   402  			mutableModFlag = "mod"
   403  		}
   404  
   405  		switch mode {
   406  		case source.LoadWorkspace, source.Normal:
   407  			if vendorEnabled {
   408  				inv.ModFlag = "vendor"
   409  			} else if !allowModfileModificationOption {
   410  				inv.ModFlag = "readonly"
   411  			} else {
   412  				inv.ModFlag = mutableModFlag
   413  			}
   414  		case source.WriteTemporaryModFile:
   415  			inv.ModFlag = mutableModFlag
   416  			// -mod must be readonly when using go.work files - see issue #48941
   417  			inv.Env = append(inv.Env, "GOWORK=off")
   418  		}
   419  	}
   420  
   421  	// Only use a temp mod file if the modfile can actually be mutated.
   422  	needTempMod := inv.ModFlag == mutableModFlag
   423  	useTempMod := s.workspaceMode()&tempModfile != 0
   424  	if needTempMod && !useTempMod {
   425  		return "", nil, cleanup, source.ErrTmpModfileUnsupported
   426  	}
   427  
   428  	// We should use -workfile if:
   429  	//  1. We're not actively trying to mutate a modfile.
   430  	//  2. We have an active go.work file.
   431  	//  3. We're using at least Go 1.18.
   432  	useWorkFile := !needTempMod && s.workspace.moduleSource == goWorkWorkspace && s.view.goversion >= 18
   433  	if useWorkFile {
   434  		// TODO(#51215): build a temp workfile and set GOWORK in the environment.
   435  	} else if useTempMod {
   436  		if modURI == "" {
   437  			return "", nil, cleanup, fmt.Errorf("no go.mod file found in %s", inv.WorkingDir)
   438  		}
   439  		modFH, err := s.GetFile(ctx, modURI)
   440  		if err != nil {
   441  			return "", nil, cleanup, err
   442  		}
   443  		// Use the go.sum if it happens to be available.
   444  		gosum := s.goSum(ctx, modURI)
   445  		tmpURI, cleanup, err = tempModFile(modFH, gosum)
   446  		if err != nil {
   447  			return "", nil, cleanup, err
   448  		}
   449  		inv.ModFile = tmpURI.Filename()
   450  	}
   451  
   452  	return tmpURI, inv, cleanup, nil
   453  }
   454  
   455  func (s *snapshot) buildOverlay() map[string][]byte {
   456  	s.mu.Lock()
   457  	defer s.mu.Unlock()
   458  
   459  	overlays := make(map[string][]byte)
   460  	for uri, fh := range s.files {
   461  		overlay, ok := fh.(*overlay)
   462  		if !ok {
   463  			continue
   464  		}
   465  		if overlay.saved {
   466  			continue
   467  		}
   468  		// TODO(rstambler): Make sure not to send overlays outside of the current view.
   469  		overlays[uri.Filename()] = overlay.text
   470  	}
   471  	return overlays
   472  }
   473  
   474  func hashUnsavedOverlays(files map[span.URI]source.VersionedFileHandle) string {
   475  	var unsaved []string
   476  	for uri, fh := range files {
   477  		if overlay, ok := fh.(*overlay); ok && !overlay.saved {
   478  			unsaved = append(unsaved, uri.Filename())
   479  		}
   480  	}
   481  	sort.Strings(unsaved)
   482  	return hashContents([]byte(strings.Join(unsaved, "")))
   483  }
   484  
   485  func (s *snapshot) PackagesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode, includeTestVariants bool) ([]source.Package, error) {
   486  	ctx = event.Label(ctx, tag.URI.Of(uri))
   487  
   488  	phs, err := s.packageHandlesForFile(ctx, uri, mode, includeTestVariants)
   489  	if err != nil {
   490  		return nil, err
   491  	}
   492  	var pkgs []source.Package
   493  	for _, ph := range phs {
   494  		pkg, err := ph.check(ctx, s)
   495  		if err != nil {
   496  			return nil, err
   497  		}
   498  		pkgs = append(pkgs, pkg)
   499  	}
   500  	return pkgs, nil
   501  }
   502  
   503  func (s *snapshot) PackageForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode, pkgPolicy source.PackageFilter) (source.Package, error) {
   504  	ctx = event.Label(ctx, tag.URI.Of(uri))
   505  
   506  	phs, err := s.packageHandlesForFile(ctx, uri, mode, false)
   507  	if err != nil {
   508  		return nil, err
   509  	}
   510  
   511  	if len(phs) < 1 {
   512  		return nil, errors.Errorf("no packages")
   513  	}
   514  
   515  	ph := phs[0]
   516  	for _, handle := range phs[1:] {
   517  		switch pkgPolicy {
   518  		case source.WidestPackage:
   519  			if ph == nil || len(handle.CompiledGoFiles()) > len(ph.CompiledGoFiles()) {
   520  				ph = handle
   521  			}
   522  		case source.NarrowestPackage:
   523  			if ph == nil || len(handle.CompiledGoFiles()) < len(ph.CompiledGoFiles()) {
   524  				ph = handle
   525  			}
   526  		}
   527  	}
   528  	if ph == nil {
   529  		return nil, errors.Errorf("no packages in input")
   530  	}
   531  
   532  	return ph.check(ctx, s)
   533  }
   534  
   535  func (s *snapshot) packageHandlesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode, includeTestVariants bool) ([]*packageHandle, error) {
   536  	// Check if we should reload metadata for the file. We don't invalidate IDs
   537  	// (though we should), so the IDs will be a better source of truth than the
   538  	// metadata. If there are no IDs for the file, then we should also reload.
   539  	fh, err := s.GetFile(ctx, uri)
   540  	if err != nil {
   541  		return nil, err
   542  	}
   543  	if kind := s.view.FileKind(fh); kind != source.Go {
   544  		return nil, fmt.Errorf("no packages for non-Go file %s (%v)", uri, kind)
   545  	}
   546  	knownIDs, err := s.getOrLoadIDsForURI(ctx, uri)
   547  	if err != nil {
   548  		return nil, err
   549  	}
   550  
   551  	var phs []*packageHandle
   552  	for _, id := range knownIDs {
   553  		// Filter out any intermediate test variants. We typically aren't
   554  		// interested in these packages for file= style queries.
   555  		if m := s.getMetadata(id); m != nil && m.IsIntermediateTestVariant && !includeTestVariants {
   556  			continue
   557  		}
   558  		var parseModes []source.ParseMode
   559  		switch mode {
   560  		case source.TypecheckAll:
   561  			if s.workspaceParseMode(id) == source.ParseFull {
   562  				parseModes = []source.ParseMode{source.ParseFull}
   563  			} else {
   564  				parseModes = []source.ParseMode{source.ParseExported, source.ParseFull}
   565  			}
   566  		case source.TypecheckFull:
   567  			parseModes = []source.ParseMode{source.ParseFull}
   568  		case source.TypecheckWorkspace:
   569  			parseModes = []source.ParseMode{s.workspaceParseMode(id)}
   570  		}
   571  
   572  		for _, parseMode := range parseModes {
   573  			ph, err := s.buildPackageHandle(ctx, id, parseMode)
   574  			if err != nil {
   575  				return nil, err
   576  			}
   577  			phs = append(phs, ph)
   578  		}
   579  	}
   580  	return phs, nil
   581  }
   582  
   583  func (s *snapshot) getOrLoadIDsForURI(ctx context.Context, uri span.URI) ([]PackageID, error) {
   584  	knownIDs := s.getIDsForURI(uri)
   585  	reload := len(knownIDs) == 0
   586  	for _, id := range knownIDs {
   587  		// Reload package metadata if any of the metadata has missing
   588  		// dependencies, in case something has changed since the last time we
   589  		// reloaded it.
   590  		if s.noValidMetadataForID(id) {
   591  			reload = true
   592  			break
   593  		}
   594  		// TODO(golang/go#36918): Previously, we would reload any package with
   595  		// missing dependencies. This is expensive and results in too many
   596  		// calls to packages.Load. Determine what we should do instead.
   597  	}
   598  	if reload {
   599  		err := s.load(ctx, false, fileURI(uri))
   600  
   601  		if !s.useInvalidMetadata() && err != nil {
   602  			return nil, err
   603  		}
   604  		// We've tried to reload and there are still no known IDs for the URI.
   605  		// Return the load error, if there was one.
   606  		knownIDs = s.getIDsForURI(uri)
   607  		if len(knownIDs) == 0 {
   608  			return nil, err
   609  		}
   610  	}
   611  	return knownIDs, nil
   612  }
   613  
   614  // Only use invalid metadata for Go versions >= 1.13. Go 1.12 and below has
   615  // issues with overlays that will cause confusing error messages if we reuse
   616  // old metadata.
   617  func (s *snapshot) useInvalidMetadata() bool {
   618  	return s.view.goversion >= 13 && s.view.Options().ExperimentalUseInvalidMetadata
   619  }
   620  
   621  func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]source.Package, error) {
   622  	if err := s.awaitLoaded(ctx); err != nil {
   623  		return nil, err
   624  	}
   625  	ids := make(map[PackageID]struct{})
   626  	s.transitiveReverseDependencies(PackageID(id), ids)
   627  
   628  	// Make sure to delete the original package ID from the map.
   629  	delete(ids, PackageID(id))
   630  
   631  	var pkgs []source.Package
   632  	for id := range ids {
   633  		pkg, err := s.checkedPackage(ctx, id, s.workspaceParseMode(id))
   634  		if err != nil {
   635  			return nil, err
   636  		}
   637  		pkgs = append(pkgs, pkg)
   638  	}
   639  	return pkgs, nil
   640  }
   641  
   642  func (s *snapshot) checkedPackage(ctx context.Context, id PackageID, mode source.ParseMode) (*pkg, error) {
   643  	ph, err := s.buildPackageHandle(ctx, id, mode)
   644  	if err != nil {
   645  		return nil, err
   646  	}
   647  	return ph.check(ctx, s)
   648  }
   649  
   650  // transitiveReverseDependencies populates the ids map with package IDs
   651  // belonging to the provided package and its transitive reverse dependencies.
   652  func (s *snapshot) transitiveReverseDependencies(id PackageID, ids map[PackageID]struct{}) {
   653  	if _, ok := ids[id]; ok {
   654  		return
   655  	}
   656  	m := s.getMetadata(id)
   657  	// Only use invalid metadata if we support it.
   658  	if m == nil || !(m.Valid || s.useInvalidMetadata()) {
   659  		return
   660  	}
   661  	ids[id] = struct{}{}
   662  	importedBy := s.getImportedBy(id)
   663  	for _, parentID := range importedBy {
   664  		s.transitiveReverseDependencies(parentID, ids)
   665  	}
   666  }
   667  
   668  func (s *snapshot) getGoFile(key parseKey) *parseGoHandle {
   669  	s.mu.Lock()
   670  	defer s.mu.Unlock()
   671  	return s.goFiles[key]
   672  }
   673  
   674  func (s *snapshot) addGoFile(key parseKey, pgh *parseGoHandle) *parseGoHandle {
   675  	s.mu.Lock()
   676  	defer s.mu.Unlock()
   677  	if existing, ok := s.goFiles[key]; ok {
   678  		return existing
   679  	}
   680  	s.goFiles[key] = pgh
   681  	return pgh
   682  }
   683  
   684  func (s *snapshot) getParseModHandle(uri span.URI) *parseModHandle {
   685  	s.mu.Lock()
   686  	defer s.mu.Unlock()
   687  	return s.parseModHandles[uri]
   688  }
   689  
   690  func (s *snapshot) getParseWorkHandle(uri span.URI) *parseWorkHandle {
   691  	s.mu.Lock()
   692  	defer s.mu.Unlock()
   693  	return s.parseWorkHandles[uri]
   694  }
   695  
   696  func (s *snapshot) getModWhyHandle(uri span.URI) *modWhyHandle {
   697  	s.mu.Lock()
   698  	defer s.mu.Unlock()
   699  	return s.modWhyHandles[uri]
   700  }
   701  
   702  func (s *snapshot) getModTidyHandle(uri span.URI) *modTidyHandle {
   703  	s.mu.Lock()
   704  	defer s.mu.Unlock()
   705  	return s.modTidyHandles[uri]
   706  }
   707  
   708  func (s *snapshot) getImportedBy(id PackageID) []PackageID {
   709  	s.mu.Lock()
   710  	defer s.mu.Unlock()
   711  	return s.getImportedByLocked(id)
   712  }
   713  
   714  func (s *snapshot) getImportedByLocked(id PackageID) []PackageID {
   715  	// If we haven't rebuilt the import graph since creating the snapshot.
   716  	if len(s.importedBy) == 0 {
   717  		s.rebuildImportGraph()
   718  	}
   719  	return s.importedBy[id]
   720  }
   721  
   722  func (s *snapshot) clearAndRebuildImportGraph() {
   723  	s.mu.Lock()
   724  	defer s.mu.Unlock()
   725  
   726  	// Completely invalidate the original map.
   727  	s.importedBy = make(map[PackageID][]PackageID)
   728  	s.rebuildImportGraph()
   729  }
   730  
   731  func (s *snapshot) rebuildImportGraph() {
   732  	for id, m := range s.metadata {
   733  		for _, importID := range m.Deps {
   734  			s.importedBy[importID] = append(s.importedBy[importID], id)
   735  		}
   736  	}
   737  }
   738  
   739  func (s *snapshot) addPackageHandle(ph *packageHandle) *packageHandle {
   740  	s.mu.Lock()
   741  	defer s.mu.Unlock()
   742  
   743  	// If the package handle has already been cached,
   744  	// return the cached handle instead of overriding it.
   745  	if ph, ok := s.packages[ph.packageKey()]; ok {
   746  		return ph
   747  	}
   748  	s.packages[ph.packageKey()] = ph
   749  	return ph
   750  }
   751  
   752  func (s *snapshot) workspacePackageIDs() (ids []PackageID) {
   753  	s.mu.Lock()
   754  	defer s.mu.Unlock()
   755  
   756  	for id := range s.workspacePackages {
   757  		ids = append(ids, id)
   758  	}
   759  	return ids
   760  }
   761  
   762  func (s *snapshot) activePackageIDs() (ids []PackageID) {
   763  	if s.view.Options().MemoryMode == source.ModeNormal {
   764  		return s.workspacePackageIDs()
   765  	}
   766  
   767  	s.mu.Lock()
   768  	defer s.mu.Unlock()
   769  
   770  	seen := make(map[PackageID]bool)
   771  	for id := range s.workspacePackages {
   772  		if s.isActiveLocked(id, seen) {
   773  			ids = append(ids, id)
   774  		}
   775  	}
   776  	return ids
   777  }
   778  
   779  func (s *snapshot) isActiveLocked(id PackageID, seen map[PackageID]bool) (active bool) {
   780  	if seen == nil {
   781  		seen = make(map[PackageID]bool)
   782  	}
   783  	if seen, ok := seen[id]; ok {
   784  		return seen
   785  	}
   786  	defer func() {
   787  		seen[id] = active
   788  	}()
   789  	m, ok := s.metadata[id]
   790  	if !ok {
   791  		return false
   792  	}
   793  	for _, cgf := range m.CompiledGoFiles {
   794  		if s.isOpenLocked(cgf) {
   795  			return true
   796  		}
   797  	}
   798  	for _, dep := range m.Deps {
   799  		if s.isActiveLocked(dep, seen) {
   800  			return true
   801  		}
   802  	}
   803  	return false
   804  }
   805  
   806  const fileExtensions = "go,mod,sum,work"
   807  
   808  func (s *snapshot) fileWatchingGlobPatterns(ctx context.Context) map[string]struct{} {
   809  	extensions := fileExtensions
   810  	for _, ext := range s.View().Options().TemplateExtensions {
   811  		extensions += "," + ext
   812  	}
   813  	// Work-around microsoft/vscode#100870 by making sure that we are,
   814  	// at least, watching the user's entire workspace. This will still be
   815  	// applied to every folder in the workspace.
   816  	patterns := map[string]struct{}{
   817  		fmt.Sprintf("**/*.{%s}", extensions): {},
   818  	}
   819  	dirs := s.workspace.dirs(ctx, s)
   820  	for _, dir := range dirs {
   821  		dirName := dir.Filename()
   822  
   823  		// If the directory is within the view's folder, we're already watching
   824  		// it with the pattern above.
   825  		if source.InDir(s.view.folder.Filename(), dirName) {
   826  			continue
   827  		}
   828  		// TODO(rstambler): If microsoft/vscode#3025 is resolved before
   829  		// microsoft/vscode#101042, we will need a work-around for Windows
   830  		// drive letter casing.
   831  		patterns[fmt.Sprintf("%s/**/*.{%s}", dirName, extensions)] = struct{}{}
   832  	}
   833  
   834  	// Some clients do not send notifications for changes to directories that
   835  	// contain Go code (golang/go#42348). To handle this, explicitly watch all
   836  	// of the directories in the workspace. We find them by adding the
   837  	// directories of every file in the snapshot's workspace directories.
   838  	var dirNames []string
   839  	for _, uri := range s.getKnownSubdirs(dirs) {
   840  		dirNames = append(dirNames, uri.Filename())
   841  	}
   842  	sort.Strings(dirNames)
   843  	if len(dirNames) > 0 {
   844  		patterns[fmt.Sprintf("{%s}", strings.Join(dirNames, ","))] = struct{}{}
   845  	}
   846  	return patterns
   847  }
   848  
   849  // collectAllKnownSubdirs collects all of the subdirectories within the
   850  // snapshot's workspace directories. None of the workspace directories are
   851  // included.
   852  func (s *snapshot) collectAllKnownSubdirs(ctx context.Context) {
   853  	dirs := s.workspace.dirs(ctx, s)
   854  
   855  	s.mu.Lock()
   856  	defer s.mu.Unlock()
   857  
   858  	s.knownSubdirs = map[span.URI]struct{}{}
   859  	for uri := range s.files {
   860  		s.addKnownSubdirLocked(uri, dirs)
   861  	}
   862  }
   863  
   864  func (s *snapshot) getKnownSubdirs(wsDirs []span.URI) []span.URI {
   865  	s.mu.Lock()
   866  	defer s.mu.Unlock()
   867  
   868  	// First, process any pending changes and update the set of known
   869  	// subdirectories.
   870  	for _, c := range s.unprocessedSubdirChanges {
   871  		if c.isUnchanged {
   872  			continue
   873  		}
   874  		if !c.exists {
   875  			s.removeKnownSubdirLocked(c.fileHandle.URI())
   876  		} else {
   877  			s.addKnownSubdirLocked(c.fileHandle.URI(), wsDirs)
   878  		}
   879  	}
   880  	s.unprocessedSubdirChanges = nil
   881  
   882  	var result []span.URI
   883  	for uri := range s.knownSubdirs {
   884  		result = append(result, uri)
   885  	}
   886  	return result
   887  }
   888  
   889  func (s *snapshot) addKnownSubdirLocked(uri span.URI, dirs []span.URI) {
   890  	dir := filepath.Dir(uri.Filename())
   891  	// First check if the directory is already known, because then we can
   892  	// return early.
   893  	if _, ok := s.knownSubdirs[span.URIFromPath(dir)]; ok {
   894  		return
   895  	}
   896  	var matched span.URI
   897  	for _, wsDir := range dirs {
   898  		if source.InDir(wsDir.Filename(), dir) {
   899  			matched = wsDir
   900  			break
   901  		}
   902  	}
   903  	// Don't watch any directory outside of the workspace directories.
   904  	if matched == "" {
   905  		return
   906  	}
   907  	for {
   908  		if dir == "" || dir == matched.Filename() {
   909  			break
   910  		}
   911  		uri := span.URIFromPath(dir)
   912  		if _, ok := s.knownSubdirs[uri]; ok {
   913  			break
   914  		}
   915  		s.knownSubdirs[uri] = struct{}{}
   916  		dir = filepath.Dir(dir)
   917  	}
   918  }
   919  
   920  func (s *snapshot) removeKnownSubdirLocked(uri span.URI) {
   921  	dir := filepath.Dir(uri.Filename())
   922  	for dir != "" {
   923  		uri := span.URIFromPath(dir)
   924  		if _, ok := s.knownSubdirs[uri]; !ok {
   925  			break
   926  		}
   927  		if info, _ := os.Stat(dir); info == nil {
   928  			delete(s.knownSubdirs, uri)
   929  		}
   930  		dir = filepath.Dir(dir)
   931  	}
   932  }
   933  
   934  // knownFilesInDir returns the files known to the given snapshot that are in
   935  // the given directory. It does not respect symlinks.
   936  func (s *snapshot) knownFilesInDir(ctx context.Context, dir span.URI) []span.URI {
   937  	var files []span.URI
   938  	s.mu.Lock()
   939  	defer s.mu.Unlock()
   940  
   941  	for uri := range s.files {
   942  		if source.InDir(dir.Filename(), uri.Filename()) {
   943  			files = append(files, uri)
   944  		}
   945  	}
   946  	return files
   947  }
   948  
   949  func (s *snapshot) workspacePackageHandles(ctx context.Context) ([]*packageHandle, error) {
   950  	if err := s.awaitLoaded(ctx); err != nil {
   951  		return nil, err
   952  	}
   953  	var phs []*packageHandle
   954  	for _, pkgID := range s.workspacePackageIDs() {
   955  		ph, err := s.buildPackageHandle(ctx, pkgID, s.workspaceParseMode(pkgID))
   956  		if err != nil {
   957  			return nil, err
   958  		}
   959  		phs = append(phs, ph)
   960  	}
   961  	return phs, nil
   962  }
   963  
   964  func (s *snapshot) ActivePackages(ctx context.Context) ([]source.Package, error) {
   965  	phs, err := s.activePackageHandles(ctx)
   966  	if err != nil {
   967  		return nil, err
   968  	}
   969  	var pkgs []source.Package
   970  	for _, ph := range phs {
   971  		pkg, err := ph.check(ctx, s)
   972  		if err != nil {
   973  			return nil, err
   974  		}
   975  		pkgs = append(pkgs, pkg)
   976  	}
   977  	return pkgs, nil
   978  }
   979  
   980  func (s *snapshot) activePackageHandles(ctx context.Context) ([]*packageHandle, error) {
   981  	if err := s.awaitLoaded(ctx); err != nil {
   982  		return nil, err
   983  	}
   984  	var phs []*packageHandle
   985  	for _, pkgID := range s.activePackageIDs() {
   986  		ph, err := s.buildPackageHandle(ctx, pkgID, s.workspaceParseMode(pkgID))
   987  		if err != nil {
   988  			return nil, err
   989  		}
   990  		phs = append(phs, ph)
   991  	}
   992  	return phs, nil
   993  }
   994  
   995  func (s *snapshot) Symbols(ctx context.Context) (map[span.URI][]source.Symbol, error) {
   996  	result := make(map[span.URI][]source.Symbol)
   997  
   998  	// Keep going on errors, but log the first failure. Partial symbol results
   999  	// are better than no symbol results.
  1000  	var firstErr error
  1001  	for uri, f := range s.files {
  1002  		sh := s.buildSymbolHandle(ctx, f)
  1003  		v, err := sh.handle.Get(ctx, s.generation, s)
  1004  		if err != nil {
  1005  			if firstErr == nil {
  1006  				firstErr = err
  1007  			}
  1008  			continue
  1009  		}
  1010  		data := v.(*symbolData)
  1011  		result[uri] = data.symbols
  1012  	}
  1013  	if firstErr != nil {
  1014  		event.Error(ctx, "getting snapshot symbols", firstErr)
  1015  	}
  1016  	return result, nil
  1017  }
  1018  
  1019  func (s *snapshot) MetadataForFile(ctx context.Context, uri span.URI) ([]source.Metadata, error) {
  1020  	knownIDs, err := s.getOrLoadIDsForURI(ctx, uri)
  1021  	if err != nil {
  1022  		return nil, err
  1023  	}
  1024  	var mds []source.Metadata
  1025  	for _, id := range knownIDs {
  1026  		md := s.getMetadata(id)
  1027  		// TODO(rfindley): knownIDs and metadata should be in sync, but existing
  1028  		// code is defensive of nil metadata.
  1029  		if md != nil {
  1030  			mds = append(mds, md)
  1031  		}
  1032  	}
  1033  	return mds, nil
  1034  }
  1035  
  1036  func (s *snapshot) KnownPackages(ctx context.Context) ([]source.Package, error) {
  1037  	if err := s.awaitLoaded(ctx); err != nil {
  1038  		return nil, err
  1039  	}
  1040  
  1041  	// The WorkspaceSymbols implementation relies on this function returning
  1042  	// workspace packages first.
  1043  	ids := s.workspacePackageIDs()
  1044  	s.mu.Lock()
  1045  	for id := range s.metadata {
  1046  		if _, ok := s.workspacePackages[id]; ok {
  1047  			continue
  1048  		}
  1049  		ids = append(ids, id)
  1050  	}
  1051  	s.mu.Unlock()
  1052  
  1053  	var pkgs []source.Package
  1054  	for _, id := range ids {
  1055  		pkg, err := s.checkedPackage(ctx, id, s.workspaceParseMode(id))
  1056  		if err != nil {
  1057  			return nil, err
  1058  		}
  1059  		pkgs = append(pkgs, pkg)
  1060  	}
  1061  	return pkgs, nil
  1062  }
  1063  
  1064  func (s *snapshot) CachedImportPaths(ctx context.Context) (map[string]source.Package, error) {
  1065  	// Don't reload workspace package metadata.
  1066  	// This function is meant to only return currently cached information.
  1067  	s.AwaitInitialized(ctx)
  1068  
  1069  	s.mu.Lock()
  1070  	defer s.mu.Unlock()
  1071  
  1072  	results := map[string]source.Package{}
  1073  	for _, ph := range s.packages {
  1074  		cachedPkg, err := ph.cached(s.generation)
  1075  		if err != nil {
  1076  			continue
  1077  		}
  1078  		for importPath, newPkg := range cachedPkg.imports {
  1079  			if oldPkg, ok := results[string(importPath)]; ok {
  1080  				// Using the same trick as NarrowestPackage, prefer non-variants.
  1081  				if len(newPkg.compiledGoFiles) < len(oldPkg.(*pkg).compiledGoFiles) {
  1082  					results[string(importPath)] = newPkg
  1083  				}
  1084  			} else {
  1085  				results[string(importPath)] = newPkg
  1086  			}
  1087  		}
  1088  	}
  1089  	return results, nil
  1090  }
  1091  
  1092  func (s *snapshot) GoModForFile(uri span.URI) span.URI {
  1093  	return moduleForURI(s.workspace.activeModFiles, uri)
  1094  }
  1095  
  1096  func moduleForURI(modFiles map[span.URI]struct{}, uri span.URI) span.URI {
  1097  	var match span.URI
  1098  	for modURI := range modFiles {
  1099  		if !source.InDir(dirURI(modURI).Filename(), uri.Filename()) {
  1100  			continue
  1101  		}
  1102  		if len(modURI) > len(match) {
  1103  			match = modURI
  1104  		}
  1105  	}
  1106  	return match
  1107  }
  1108  
  1109  func (s *snapshot) getPackage(id PackageID, mode source.ParseMode) *packageHandle {
  1110  	s.mu.Lock()
  1111  	defer s.mu.Unlock()
  1112  
  1113  	key := packageKey{
  1114  		id:   id,
  1115  		mode: mode,
  1116  	}
  1117  	return s.packages[key]
  1118  }
  1119  
  1120  func (s *snapshot) getSymbolHandle(uri span.URI) *symbolHandle {
  1121  	s.mu.Lock()
  1122  	defer s.mu.Unlock()
  1123  
  1124  	return s.symbols[uri]
  1125  }
  1126  
  1127  func (s *snapshot) addSymbolHandle(sh *symbolHandle) *symbolHandle {
  1128  	s.mu.Lock()
  1129  	defer s.mu.Unlock()
  1130  
  1131  	uri := sh.fh.URI()
  1132  	// If the package handle has already been cached,
  1133  	// return the cached handle instead of overriding it.
  1134  	if sh, ok := s.symbols[uri]; ok {
  1135  		return sh
  1136  	}
  1137  	s.symbols[uri] = sh
  1138  	return sh
  1139  }
  1140  
  1141  func (s *snapshot) getActionHandle(id PackageID, m source.ParseMode, a *analysis.Analyzer) *actionHandle {
  1142  	s.mu.Lock()
  1143  	defer s.mu.Unlock()
  1144  
  1145  	key := actionKey{
  1146  		pkg: packageKey{
  1147  			id:   id,
  1148  			mode: m,
  1149  		},
  1150  		analyzer: a,
  1151  	}
  1152  	return s.actions[key]
  1153  }
  1154  
  1155  func (s *snapshot) addActionHandle(ah *actionHandle) *actionHandle {
  1156  	s.mu.Lock()
  1157  	defer s.mu.Unlock()
  1158  
  1159  	key := actionKey{
  1160  		analyzer: ah.analyzer,
  1161  		pkg: packageKey{
  1162  			id:   ah.pkg.m.ID,
  1163  			mode: ah.pkg.mode,
  1164  		},
  1165  	}
  1166  	if ah, ok := s.actions[key]; ok {
  1167  		return ah
  1168  	}
  1169  	s.actions[key] = ah
  1170  	return ah
  1171  }
  1172  
  1173  func (s *snapshot) getIDsForURI(uri span.URI) []PackageID {
  1174  	s.mu.Lock()
  1175  	defer s.mu.Unlock()
  1176  
  1177  	return s.ids[uri]
  1178  }
  1179  
  1180  func (s *snapshot) getMetadata(id PackageID) *KnownMetadata {
  1181  	s.mu.Lock()
  1182  	defer s.mu.Unlock()
  1183  
  1184  	return s.metadata[id]
  1185  }
  1186  
  1187  func (s *snapshot) shouldLoad(scope interface{}) bool {
  1188  	s.mu.Lock()
  1189  	defer s.mu.Unlock()
  1190  
  1191  	switch scope := scope.(type) {
  1192  	case PackagePath:
  1193  		var meta *KnownMetadata
  1194  		for _, m := range s.metadata {
  1195  			if m.PkgPath != scope {
  1196  				continue
  1197  			}
  1198  			meta = m
  1199  		}
  1200  		if meta == nil || meta.ShouldLoad {
  1201  			return true
  1202  		}
  1203  		return false
  1204  	case fileURI:
  1205  		uri := span.URI(scope)
  1206  		ids := s.ids[uri]
  1207  		if len(ids) == 0 {
  1208  			return true
  1209  		}
  1210  		for _, id := range ids {
  1211  			m, ok := s.metadata[id]
  1212  			if !ok || m.ShouldLoad {
  1213  				return true
  1214  			}
  1215  		}
  1216  		return false
  1217  	default:
  1218  		return true
  1219  	}
  1220  }
  1221  
  1222  func (s *snapshot) clearShouldLoad(scope interface{}) {
  1223  	s.mu.Lock()
  1224  	defer s.mu.Unlock()
  1225  
  1226  	switch scope := scope.(type) {
  1227  	case PackagePath:
  1228  		var meta *KnownMetadata
  1229  		for _, m := range s.metadata {
  1230  			if m.PkgPath == scope {
  1231  				meta = m
  1232  			}
  1233  		}
  1234  		if meta == nil {
  1235  			return
  1236  		}
  1237  		meta.ShouldLoad = false
  1238  	case fileURI:
  1239  		uri := span.URI(scope)
  1240  		ids := s.ids[uri]
  1241  		if len(ids) == 0 {
  1242  			return
  1243  		}
  1244  		for _, id := range ids {
  1245  			if m, ok := s.metadata[id]; ok {
  1246  				m.ShouldLoad = false
  1247  			}
  1248  		}
  1249  	}
  1250  }
  1251  
  1252  // noValidMetadataForURILocked reports whether there is any valid metadata for
  1253  // the given URI.
  1254  func (s *snapshot) noValidMetadataForURILocked(uri span.URI) bool {
  1255  	ids, ok := s.ids[uri]
  1256  	if !ok {
  1257  		return true
  1258  	}
  1259  	for _, id := range ids {
  1260  		if m, ok := s.metadata[id]; ok && m.Valid {
  1261  			return false
  1262  		}
  1263  	}
  1264  	return true
  1265  }
  1266  
  1267  // noValidMetadataForID reports whether there is no valid metadata for the
  1268  // given ID.
  1269  func (s *snapshot) noValidMetadataForID(id PackageID) bool {
  1270  	s.mu.Lock()
  1271  	defer s.mu.Unlock()
  1272  	return s.noValidMetadataForIDLocked(id)
  1273  }
  1274  
  1275  func (s *snapshot) noValidMetadataForIDLocked(id PackageID) bool {
  1276  	m := s.metadata[id]
  1277  	return m == nil || !m.Valid
  1278  }
  1279  
  1280  // updateIDForURIsLocked adds the given ID to the set of known IDs for the given URI.
  1281  // Any existing invalid IDs are removed from the set of known IDs. IDs that are
  1282  // not "command-line-arguments" are preferred, so if a new ID comes in for a
  1283  // URI that previously only had "command-line-arguments", the new ID will
  1284  // replace the "command-line-arguments" ID.
  1285  func (s *snapshot) updateIDForURIsLocked(id PackageID, uris map[span.URI]struct{}) {
  1286  	for uri := range uris {
  1287  		// Collect the new set of IDs, preserving any valid existing IDs.
  1288  		newIDs := []PackageID{id}
  1289  		for _, existingID := range s.ids[uri] {
  1290  			// Don't set duplicates of the same ID.
  1291  			if existingID == id {
  1292  				continue
  1293  			}
  1294  			// If the package previously only had a command-line-arguments ID,
  1295  			// delete the command-line-arguments workspace package.
  1296  			if source.IsCommandLineArguments(string(existingID)) {
  1297  				delete(s.workspacePackages, existingID)
  1298  				continue
  1299  			}
  1300  			// If the metadata for an existing ID is invalid, and we are
  1301  			// setting metadata for a new, valid ID--don't preserve the old ID.
  1302  			if m, ok := s.metadata[existingID]; !ok || !m.Valid {
  1303  				continue
  1304  			}
  1305  			newIDs = append(newIDs, existingID)
  1306  		}
  1307  		sort.Slice(newIDs, func(i, j int) bool {
  1308  			return newIDs[i] < newIDs[j]
  1309  		})
  1310  		s.ids[uri] = newIDs
  1311  	}
  1312  }
  1313  
  1314  func (s *snapshot) isWorkspacePackage(id PackageID) bool {
  1315  	s.mu.Lock()
  1316  	defer s.mu.Unlock()
  1317  
  1318  	_, ok := s.workspacePackages[id]
  1319  	return ok
  1320  }
  1321  
  1322  func (s *snapshot) FindFile(uri span.URI) source.VersionedFileHandle {
  1323  	f := s.view.getFile(uri)
  1324  
  1325  	s.mu.Lock()
  1326  	defer s.mu.Unlock()
  1327  
  1328  	return s.files[f.URI()]
  1329  }
  1330  
  1331  // GetVersionedFile returns a File for the given URI. If the file is unknown it
  1332  // is added to the managed set.
  1333  //
  1334  // GetVersionedFile succeeds even if the file does not exist. A non-nil error return
  1335  // indicates some type of internal error, for example if ctx is cancelled.
  1336  func (s *snapshot) GetVersionedFile(ctx context.Context, uri span.URI) (source.VersionedFileHandle, error) {
  1337  	f := s.view.getFile(uri)
  1338  
  1339  	s.mu.Lock()
  1340  	defer s.mu.Unlock()
  1341  	return s.getFileLocked(ctx, f)
  1342  }
  1343  
  1344  // GetFile implements the fileSource interface by wrapping GetVersionedFile.
  1345  func (s *snapshot) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
  1346  	return s.GetVersionedFile(ctx, uri)
  1347  }
  1348  
  1349  func (s *snapshot) getFileLocked(ctx context.Context, f *fileBase) (source.VersionedFileHandle, error) {
  1350  	if fh, ok := s.files[f.URI()]; ok {
  1351  		return fh, nil
  1352  	}
  1353  
  1354  	fh, err := s.view.session.cache.getFile(ctx, f.URI())
  1355  	if err != nil {
  1356  		return nil, err
  1357  	}
  1358  	closed := &closedFile{fh}
  1359  	s.files[f.URI()] = closed
  1360  	return closed, nil
  1361  }
  1362  
  1363  func (s *snapshot) IsOpen(uri span.URI) bool {
  1364  	s.mu.Lock()
  1365  	defer s.mu.Unlock()
  1366  	return s.isOpenLocked(uri)
  1367  
  1368  }
  1369  
  1370  func (s *snapshot) openFiles() []source.VersionedFileHandle {
  1371  	s.mu.Lock()
  1372  	defer s.mu.Unlock()
  1373  
  1374  	var open []source.VersionedFileHandle
  1375  	for _, fh := range s.files {
  1376  		if s.isOpenLocked(fh.URI()) {
  1377  			open = append(open, fh)
  1378  		}
  1379  	}
  1380  	return open
  1381  }
  1382  
  1383  func (s *snapshot) isOpenLocked(uri span.URI) bool {
  1384  	_, open := s.files[uri].(*overlay)
  1385  	return open
  1386  }
  1387  
  1388  func (s *snapshot) awaitLoaded(ctx context.Context) error {
  1389  	loadErr := s.awaitLoadedAllErrors(ctx)
  1390  
  1391  	s.mu.Lock()
  1392  	defer s.mu.Unlock()
  1393  
  1394  	// If we still have absolutely no metadata, check if the view failed to
  1395  	// initialize and return any errors.
  1396  	if s.useInvalidMetadata() && len(s.metadata) > 0 {
  1397  		return nil
  1398  	}
  1399  	for _, m := range s.metadata {
  1400  		if m.Valid {
  1401  			return nil
  1402  		}
  1403  	}
  1404  	if loadErr != nil {
  1405  		return loadErr.MainError
  1406  	}
  1407  	return nil
  1408  }
  1409  
  1410  func (s *snapshot) GetCriticalError(ctx context.Context) *source.CriticalError {
  1411  	loadErr := s.awaitLoadedAllErrors(ctx)
  1412  	if loadErr != nil && errors.Is(loadErr.MainError, context.Canceled) {
  1413  		return nil
  1414  	}
  1415  
  1416  	// Even if packages didn't fail to load, we still may want to show
  1417  	// additional warnings.
  1418  	if loadErr == nil {
  1419  		wsPkgs, _ := s.ActivePackages(ctx)
  1420  		if msg := shouldShowAdHocPackagesWarning(s, wsPkgs); msg != "" {
  1421  			return &source.CriticalError{
  1422  				MainError: errors.New(msg),
  1423  			}
  1424  		}
  1425  		// Even if workspace packages were returned, there still may be an error
  1426  		// with the user's workspace layout. Workspace packages that only have the
  1427  		// ID "command-line-arguments" are usually a symptom of a bad workspace
  1428  		// configuration.
  1429  		if containsCommandLineArguments(wsPkgs) {
  1430  			return s.workspaceLayoutError(ctx)
  1431  		}
  1432  		return nil
  1433  	}
  1434  
  1435  	if errMsg := loadErr.MainError.Error(); strings.Contains(errMsg, "cannot find main module") || strings.Contains(errMsg, "go.mod file not found") {
  1436  		return s.workspaceLayoutError(ctx)
  1437  	}
  1438  	return loadErr
  1439  }
  1440  
  1441  const adHocPackagesWarning = `You are outside of a module and outside of $GOPATH/src.
  1442  If you are using modules, please open your editor to a directory in your module.
  1443  If you believe this warning is incorrect, please file an issue: https://github.com/golang/go/issues/new.`
  1444  
  1445  func shouldShowAdHocPackagesWarning(snapshot source.Snapshot, pkgs []source.Package) string {
  1446  	if snapshot.ValidBuildConfiguration() {
  1447  		return ""
  1448  	}
  1449  	for _, pkg := range pkgs {
  1450  		if len(pkg.MissingDependencies()) > 0 {
  1451  			return adHocPackagesWarning
  1452  		}
  1453  	}
  1454  	return ""
  1455  }
  1456  
  1457  func containsCommandLineArguments(pkgs []source.Package) bool {
  1458  	for _, pkg := range pkgs {
  1459  		if source.IsCommandLineArguments(pkg.ID()) {
  1460  			return true
  1461  		}
  1462  	}
  1463  	return false
  1464  }
  1465  
  1466  func (s *snapshot) awaitLoadedAllErrors(ctx context.Context) *source.CriticalError {
  1467  	// Do not return results until the snapshot's view has been initialized.
  1468  	s.AwaitInitialized(ctx)
  1469  
  1470  	// TODO(rstambler): Should we be more careful about returning the
  1471  	// initialization error? Is it possible for the initialization error to be
  1472  	// corrected without a successful reinitialization?
  1473  	s.mu.Lock()
  1474  	initializedErr := s.initializedErr
  1475  	s.mu.Unlock()
  1476  	if initializedErr != nil {
  1477  		return initializedErr
  1478  	}
  1479  
  1480  	if ctx.Err() != nil {
  1481  		return &source.CriticalError{MainError: ctx.Err()}
  1482  	}
  1483  
  1484  	if err := s.reloadWorkspace(ctx); err != nil {
  1485  		diags, _ := s.extractGoCommandErrors(ctx, err.Error())
  1486  		return &source.CriticalError{
  1487  			MainError: err,
  1488  			DiagList:  diags,
  1489  		}
  1490  	}
  1491  	if err := s.reloadOrphanedFiles(ctx); err != nil {
  1492  		diags, _ := s.extractGoCommandErrors(ctx, err.Error())
  1493  		return &source.CriticalError{
  1494  			MainError: err,
  1495  			DiagList:  diags,
  1496  		}
  1497  	}
  1498  	return nil
  1499  }
  1500  
  1501  func (s *snapshot) getInitializationError(ctx context.Context) *source.CriticalError {
  1502  	s.mu.Lock()
  1503  	defer s.mu.Unlock()
  1504  
  1505  	return s.initializedErr
  1506  }
  1507  
  1508  func (s *snapshot) AwaitInitialized(ctx context.Context) {
  1509  	select {
  1510  	case <-ctx.Done():
  1511  		return
  1512  	case <-s.view.initialWorkspaceLoad:
  1513  	}
  1514  	// We typically prefer to run something as intensive as the IWL without
  1515  	// blocking. I'm not sure if there is a way to do that here.
  1516  	s.initialize(ctx, false)
  1517  }
  1518  
  1519  // reloadWorkspace reloads the metadata for all invalidated workspace packages.
  1520  func (s *snapshot) reloadWorkspace(ctx context.Context) error {
  1521  	// See which of the workspace packages are missing metadata.
  1522  	s.mu.Lock()
  1523  	missingMetadata := len(s.workspacePackages) == 0 || len(s.metadata) == 0
  1524  	pkgPathSet := map[PackagePath]struct{}{}
  1525  	for id, pkgPath := range s.workspacePackages {
  1526  		if m, ok := s.metadata[id]; ok && m.Valid {
  1527  			continue
  1528  		}
  1529  		missingMetadata = true
  1530  
  1531  		// Don't try to reload "command-line-arguments" directly.
  1532  		if source.IsCommandLineArguments(string(pkgPath)) {
  1533  			continue
  1534  		}
  1535  		pkgPathSet[pkgPath] = struct{}{}
  1536  	}
  1537  	s.mu.Unlock()
  1538  
  1539  	// If the view's build configuration is invalid, we cannot reload by
  1540  	// package path. Just reload the directory instead.
  1541  	if missingMetadata && !s.ValidBuildConfiguration() {
  1542  		return s.load(ctx, false, viewLoadScope("LOAD_INVALID_VIEW"))
  1543  	}
  1544  
  1545  	if len(pkgPathSet) == 0 {
  1546  		return nil
  1547  	}
  1548  
  1549  	var pkgPaths []interface{}
  1550  	for pkgPath := range pkgPathSet {
  1551  		pkgPaths = append(pkgPaths, pkgPath)
  1552  	}
  1553  	return s.load(ctx, false, pkgPaths...)
  1554  }
  1555  
  1556  func (s *snapshot) reloadOrphanedFiles(ctx context.Context) error {
  1557  	// When we load ./... or a package path directly, we may not get packages
  1558  	// that exist only in overlays. As a workaround, we search all of the files
  1559  	// available in the snapshot and reload their metadata individually using a
  1560  	// file= query if the metadata is unavailable.
  1561  	files := s.orphanedFiles()
  1562  
  1563  	// Files without a valid package declaration can't be loaded. Don't try.
  1564  	var scopes []interface{}
  1565  	for _, file := range files {
  1566  		pgf, err := s.ParseGo(ctx, file, source.ParseHeader)
  1567  		if err != nil {
  1568  			continue
  1569  		}
  1570  		if !pgf.File.Package.IsValid() {
  1571  			continue
  1572  		}
  1573  		scopes = append(scopes, fileURI(file.URI()))
  1574  	}
  1575  
  1576  	if len(scopes) == 0 {
  1577  		return nil
  1578  	}
  1579  
  1580  	// The regtests match this exact log message, keep them in sync.
  1581  	event.Log(ctx, "reloadOrphanedFiles reloading", tag.Query.Of(scopes))
  1582  	err := s.load(ctx, false, scopes...)
  1583  
  1584  	// If we failed to load some files, i.e. they have no metadata,
  1585  	// mark the failures so we don't bother retrying until the file's
  1586  	// content changes.
  1587  	//
  1588  	// TODO(rstambler): This may be an overestimate if the load stopped
  1589  	// early for an unrelated errors. Add a fallback?
  1590  	//
  1591  	// Check for context cancellation so that we don't incorrectly mark files
  1592  	// as unloadable, but don't return before setting all workspace packages.
  1593  	if ctx.Err() == nil && err != nil {
  1594  		event.Error(ctx, "reloadOrphanedFiles: failed to load", err, tag.Query.Of(scopes))
  1595  		s.mu.Lock()
  1596  		for _, scope := range scopes {
  1597  			uri := span.URI(scope.(fileURI))
  1598  			if s.noValidMetadataForURILocked(uri) {
  1599  				s.unloadableFiles[uri] = struct{}{}
  1600  			}
  1601  		}
  1602  		s.mu.Unlock()
  1603  	}
  1604  	return nil
  1605  }
  1606  
  1607  func (s *snapshot) orphanedFiles() []source.VersionedFileHandle {
  1608  	s.mu.Lock()
  1609  	defer s.mu.Unlock()
  1610  
  1611  	var files []source.VersionedFileHandle
  1612  	for uri, fh := range s.files {
  1613  		// Don't try to reload metadata for go.mod files.
  1614  		if s.view.FileKind(fh) != source.Go {
  1615  			continue
  1616  		}
  1617  		// If the URI doesn't belong to this view, then it's not in a workspace
  1618  		// package and should not be reloaded directly.
  1619  		if !contains(s.view.session.viewsOf(uri), s.view) {
  1620  			continue
  1621  		}
  1622  		// If the file is not open and is in a vendor directory, don't treat it
  1623  		// like a workspace package.
  1624  		if _, ok := fh.(*overlay); !ok && inVendor(uri) {
  1625  			continue
  1626  		}
  1627  		// Don't reload metadata for files we've already deemed unloadable.
  1628  		if _, ok := s.unloadableFiles[uri]; ok {
  1629  			continue
  1630  		}
  1631  		if s.noValidMetadataForURILocked(uri) {
  1632  			files = append(files, fh)
  1633  		}
  1634  	}
  1635  	return files
  1636  }
  1637  
  1638  func contains(views []*View, view *View) bool {
  1639  	for _, v := range views {
  1640  		if v == view {
  1641  			return true
  1642  		}
  1643  	}
  1644  	return false
  1645  }
  1646  
  1647  func inVendor(uri span.URI) bool {
  1648  	if !strings.Contains(string(uri), "/vendor/") {
  1649  		return false
  1650  	}
  1651  	// Only packages in _subdirectories_ of /vendor/ are considered vendored
  1652  	// (/vendor/a/foo.go is vendored, /vendor/foo.go is not).
  1653  	split := strings.Split(string(uri), "/vendor/")
  1654  	if len(split) < 2 {
  1655  		return false
  1656  	}
  1657  	return strings.Contains(split[1], "/")
  1658  }
  1659  
  1660  func generationName(v *View, snapshotID uint64) string {
  1661  	return fmt.Sprintf("v%v/%v", v.id, snapshotID)
  1662  }
  1663  
  1664  // checkSnapshotLocked verifies that some invariants are preserved on the
  1665  // snapshot.
  1666  func checkSnapshotLocked(ctx context.Context, s *snapshot) {
  1667  	// Check that every go file for a workspace package is identified as
  1668  	// belonging to that workspace package.
  1669  	for wsID := range s.workspacePackages {
  1670  		if m, ok := s.metadata[wsID]; ok {
  1671  			for _, uri := range m.GoFiles {
  1672  				found := false
  1673  				for _, id := range s.ids[uri] {
  1674  					if id == wsID {
  1675  						found = true
  1676  						break
  1677  					}
  1678  				}
  1679  				if !found {
  1680  					log.Error.Logf(ctx, "workspace package %v not associated with %v", wsID, uri)
  1681  				}
  1682  			}
  1683  		}
  1684  	}
  1685  }
  1686  
  1687  // unappliedChanges is a file source that handles an uncloned snapshot.
  1688  type unappliedChanges struct {
  1689  	originalSnapshot *snapshot
  1690  	changes          map[span.URI]*fileChange
  1691  }
  1692  
  1693  func (ac *unappliedChanges) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
  1694  	if c, ok := ac.changes[uri]; ok {
  1695  		return c.fileHandle, nil
  1696  	}
  1697  	return ac.originalSnapshot.GetFile(ctx, uri)
  1698  }
  1699  
  1700  func (s *snapshot) clone(ctx, bgCtx context.Context, changes map[span.URI]*fileChange, forceReloadMetadata bool) *snapshot {
  1701  	var vendorChanged bool
  1702  	newWorkspace, workspaceChanged, workspaceReload := s.workspace.invalidate(ctx, changes, &unappliedChanges{
  1703  		originalSnapshot: s,
  1704  		changes:          changes,
  1705  	})
  1706  
  1707  	s.mu.Lock()
  1708  	defer s.mu.Unlock()
  1709  
  1710  	checkSnapshotLocked(ctx, s)
  1711  
  1712  	newGen := s.view.session.cache.store.Generation(generationName(s.view, s.id+1))
  1713  	bgCtx, cancel := context.WithCancel(bgCtx)
  1714  	result := &snapshot{
  1715  		id:                s.id + 1,
  1716  		generation:        newGen,
  1717  		view:              s.view,
  1718  		backgroundCtx:     bgCtx,
  1719  		cancel:            cancel,
  1720  		builtin:           s.builtin,
  1721  		initializeOnce:    s.initializeOnce,
  1722  		initializedErr:    s.initializedErr,
  1723  		ids:               make(map[span.URI][]PackageID, len(s.ids)),
  1724  		importedBy:        make(map[PackageID][]PackageID, len(s.importedBy)),
  1725  		metadata:          make(map[PackageID]*KnownMetadata, len(s.metadata)),
  1726  		packages:          make(map[packageKey]*packageHandle, len(s.packages)),
  1727  		actions:           make(map[actionKey]*actionHandle, len(s.actions)),
  1728  		files:             make(map[span.URI]source.VersionedFileHandle, len(s.files)),
  1729  		goFiles:           make(map[parseKey]*parseGoHandle, len(s.goFiles)),
  1730  		symbols:           make(map[span.URI]*symbolHandle, len(s.symbols)),
  1731  		workspacePackages: make(map[PackageID]PackagePath, len(s.workspacePackages)),
  1732  		unloadableFiles:   make(map[span.URI]struct{}, len(s.unloadableFiles)),
  1733  		parseModHandles:   make(map[span.URI]*parseModHandle, len(s.parseModHandles)),
  1734  		parseWorkHandles:  make(map[span.URI]*parseWorkHandle, len(s.parseWorkHandles)),
  1735  		modTidyHandles:    make(map[span.URI]*modTidyHandle, len(s.modTidyHandles)),
  1736  		modWhyHandles:     make(map[span.URI]*modWhyHandle, len(s.modWhyHandles)),
  1737  		knownSubdirs:      make(map[span.URI]struct{}, len(s.knownSubdirs)),
  1738  		workspace:         newWorkspace,
  1739  	}
  1740  
  1741  	if !workspaceChanged && s.workspaceDirHandle != nil {
  1742  		result.workspaceDirHandle = s.workspaceDirHandle
  1743  		newGen.Inherit(s.workspaceDirHandle)
  1744  	}
  1745  
  1746  	// Copy all of the FileHandles.
  1747  	for k, v := range s.files {
  1748  		result.files[k] = v
  1749  	}
  1750  	for k, v := range s.symbols {
  1751  		if change, ok := changes[k]; ok {
  1752  			if change.exists {
  1753  				result.symbols[k] = result.buildSymbolHandle(ctx, change.fileHandle)
  1754  			}
  1755  			continue
  1756  		}
  1757  		newGen.Inherit(v.handle)
  1758  		result.symbols[k] = v
  1759  	}
  1760  
  1761  	// Copy the set of unloadable files.
  1762  	for k, v := range s.unloadableFiles {
  1763  		result.unloadableFiles[k] = v
  1764  	}
  1765  	// Copy all of the modHandles.
  1766  	for k, v := range s.parseModHandles {
  1767  		result.parseModHandles[k] = v
  1768  	}
  1769  	// Copy all of the parseWorkHandles.
  1770  	for k, v := range s.parseWorkHandles {
  1771  		result.parseWorkHandles[k] = v
  1772  	}
  1773  
  1774  	for k, v := range s.goFiles {
  1775  		if _, ok := changes[k.file.URI]; ok {
  1776  			continue
  1777  		}
  1778  		newGen.Inherit(v.handle)
  1779  		result.goFiles[k] = v
  1780  	}
  1781  
  1782  	// Copy all of the go.mod-related handles. They may be invalidated later,
  1783  	// so we inherit them at the end of the function.
  1784  	for k, v := range s.modTidyHandles {
  1785  		if _, ok := changes[k]; ok {
  1786  			continue
  1787  		}
  1788  		result.modTidyHandles[k] = v
  1789  	}
  1790  	for k, v := range s.modWhyHandles {
  1791  		if _, ok := changes[k]; ok {
  1792  			continue
  1793  		}
  1794  		result.modWhyHandles[k] = v
  1795  	}
  1796  
  1797  	// Add all of the known subdirectories, but don't update them for the
  1798  	// changed files. We need to rebuild the workspace module to know the
  1799  	// true set of known subdirectories, but we don't want to do that in clone.
  1800  	for k, v := range s.knownSubdirs {
  1801  		result.knownSubdirs[k] = v
  1802  	}
  1803  	for _, c := range changes {
  1804  		result.unprocessedSubdirChanges = append(result.unprocessedSubdirChanges, c)
  1805  	}
  1806  
  1807  	// directIDs keeps track of package IDs that have directly changed.
  1808  	// It maps id->invalidateMetadata.
  1809  	directIDs := map[PackageID]bool{}
  1810  
  1811  	// Invalidate all package metadata if the workspace module has changed.
  1812  	if workspaceReload {
  1813  		for k := range s.metadata {
  1814  			directIDs[k] = true
  1815  		}
  1816  	}
  1817  
  1818  	changedPkgNames := map[PackageID]struct{}{}
  1819  	anyImportDeleted := false
  1820  	for uri, change := range changes {
  1821  		// Maybe reinitialize the view if we see a change in the vendor
  1822  		// directory.
  1823  		if inVendor(uri) {
  1824  			vendorChanged = true
  1825  		}
  1826  
  1827  		// The original FileHandle for this URI is cached on the snapshot.
  1828  		originalFH := s.files[uri]
  1829  
  1830  		// Check if the file's package name or imports have changed,
  1831  		// and if so, invalidate this file's packages' metadata.
  1832  		var shouldInvalidateMetadata, pkgNameChanged, importDeleted bool
  1833  		if !isGoMod(uri) {
  1834  			shouldInvalidateMetadata, pkgNameChanged, importDeleted = s.shouldInvalidateMetadata(ctx, result, originalFH, change.fileHandle)
  1835  		}
  1836  		invalidateMetadata := forceReloadMetadata || workspaceReload || shouldInvalidateMetadata
  1837  		anyImportDeleted = anyImportDeleted || importDeleted
  1838  
  1839  		// Mark all of the package IDs containing the given file.
  1840  		// TODO: if the file has moved into a new package, we should invalidate that too.
  1841  		filePackageIDs := guessPackageIDsForURI(uri, s.ids)
  1842  		if pkgNameChanged {
  1843  			for _, id := range filePackageIDs {
  1844  				changedPkgNames[id] = struct{}{}
  1845  			}
  1846  		}
  1847  		for _, id := range filePackageIDs {
  1848  			directIDs[id] = directIDs[id] || invalidateMetadata
  1849  		}
  1850  
  1851  		// Invalidate the previous modTidyHandle if any of the files have been
  1852  		// saved or if any of the metadata has been invalidated.
  1853  		if invalidateMetadata || fileWasSaved(originalFH, change.fileHandle) {
  1854  			// TODO(rstambler): Only delete mod handles for which the
  1855  			// withoutURI is relevant.
  1856  			for k := range s.modTidyHandles {
  1857  				delete(result.modTidyHandles, k)
  1858  			}
  1859  			for k := range s.modWhyHandles {
  1860  				delete(result.modWhyHandles, k)
  1861  			}
  1862  		}
  1863  		delete(result.parseModHandles, uri)
  1864  		delete(result.parseWorkHandles, uri)
  1865  		// Handle the invalidated file; it may have new contents or not exist.
  1866  		if !change.exists {
  1867  			delete(result.files, uri)
  1868  		} else {
  1869  			result.files[uri] = change.fileHandle
  1870  		}
  1871  
  1872  		// Make sure to remove the changed file from the unloadable set.
  1873  		delete(result.unloadableFiles, uri)
  1874  	}
  1875  
  1876  	// Deleting an import can cause list errors due to import cycles to be
  1877  	// resolved. The best we can do without parsing the list error message is to
  1878  	// hope that list errors may have been resolved by a deleted import.
  1879  	//
  1880  	// We could do better by parsing the list error message. We already do this
  1881  	// to assign a better range to the list error, but for such critical
  1882  	// functionality as metadata, it's better to be conservative until it proves
  1883  	// impractical.
  1884  	//
  1885  	// We could also do better by looking at which imports were deleted and
  1886  	// trying to find cycles they are involved in. This fails when the file goes
  1887  	// from an unparseable state to a parseable state, as we don't have a
  1888  	// starting point to compare with.
  1889  	if anyImportDeleted {
  1890  		for id, metadata := range s.metadata {
  1891  			if len(metadata.Errors) > 0 {
  1892  				directIDs[id] = true
  1893  			}
  1894  		}
  1895  	}
  1896  
  1897  	// Invalidate reverse dependencies too.
  1898  	// TODO(heschi): figure out the locking model and use transitiveReverseDeps?
  1899  	// idsToInvalidate keeps track of transitive reverse dependencies.
  1900  	// If an ID is present in the map, invalidate its types.
  1901  	// If an ID's value is true, invalidate its metadata too.
  1902  	idsToInvalidate := map[PackageID]bool{}
  1903  	var addRevDeps func(PackageID, bool)
  1904  	addRevDeps = func(id PackageID, invalidateMetadata bool) {
  1905  		current, seen := idsToInvalidate[id]
  1906  		newInvalidateMetadata := current || invalidateMetadata
  1907  
  1908  		// If we've already seen this ID, and the value of invalidate
  1909  		// metadata has not changed, we can return early.
  1910  		if seen && current == newInvalidateMetadata {
  1911  			return
  1912  		}
  1913  		idsToInvalidate[id] = newInvalidateMetadata
  1914  		for _, rid := range s.getImportedByLocked(id) {
  1915  			addRevDeps(rid, invalidateMetadata)
  1916  		}
  1917  	}
  1918  	for id, invalidateMetadata := range directIDs {
  1919  		addRevDeps(id, invalidateMetadata)
  1920  	}
  1921  
  1922  	// Copy the package type information.
  1923  	for k, v := range s.packages {
  1924  		if _, ok := idsToInvalidate[k.id]; ok {
  1925  			continue
  1926  		}
  1927  		newGen.Inherit(v.handle)
  1928  		result.packages[k] = v
  1929  	}
  1930  	// Copy the package analysis information.
  1931  	for k, v := range s.actions {
  1932  		if _, ok := idsToInvalidate[k.pkg.id]; ok {
  1933  			continue
  1934  		}
  1935  		newGen.Inherit(v.handle)
  1936  		result.actions[k] = v
  1937  	}
  1938  
  1939  	// If the workspace mode has changed, we must delete all metadata, as it
  1940  	// is unusable and may produce confusing or incorrect diagnostics.
  1941  	// If a file has been deleted, we must delete metadata all packages
  1942  	// containing that file.
  1943  	workspaceModeChanged := s.workspaceMode() != result.workspaceMode()
  1944  	skipID := map[PackageID]bool{}
  1945  	for _, c := range changes {
  1946  		if c.exists {
  1947  			continue
  1948  		}
  1949  		// The file has been deleted.
  1950  		if ids, ok := s.ids[c.fileHandle.URI()]; ok {
  1951  			for _, id := range ids {
  1952  				skipID[id] = true
  1953  			}
  1954  		}
  1955  	}
  1956  
  1957  	// Collect all of the IDs that are reachable from the workspace packages.
  1958  	// Any unreachable IDs will have their metadata deleted outright.
  1959  	reachableID := map[PackageID]bool{}
  1960  	var addForwardDeps func(PackageID)
  1961  	addForwardDeps = func(id PackageID) {
  1962  		if reachableID[id] {
  1963  			return
  1964  		}
  1965  		reachableID[id] = true
  1966  		m, ok := s.metadata[id]
  1967  		if !ok {
  1968  			return
  1969  		}
  1970  		for _, depID := range m.Deps {
  1971  			addForwardDeps(depID)
  1972  		}
  1973  	}
  1974  	for id := range s.workspacePackages {
  1975  		addForwardDeps(id)
  1976  	}
  1977  
  1978  	// Copy the URI to package ID mappings, skipping only those URIs whose
  1979  	// metadata will be reloaded in future calls to load.
  1980  	deleteInvalidMetadata := forceReloadMetadata || workspaceModeChanged
  1981  	idsInSnapshot := map[PackageID]bool{} // track all known IDs
  1982  	for uri, ids := range s.ids {
  1983  		for _, id := range ids {
  1984  			invalidateMetadata := idsToInvalidate[id]
  1985  			if skipID[id] || (invalidateMetadata && deleteInvalidMetadata) {
  1986  				continue
  1987  			}
  1988  			// The ID is not reachable from any workspace package, so it should
  1989  			// be deleted.
  1990  			if !reachableID[id] {
  1991  				continue
  1992  			}
  1993  			idsInSnapshot[id] = true
  1994  			result.ids[uri] = append(result.ids[uri], id)
  1995  		}
  1996  	}
  1997  
  1998  	// Copy the package metadata. We only need to invalidate packages directly
  1999  	// containing the affected file, and only if it changed in a relevant way.
  2000  	for k, v := range s.metadata {
  2001  		if !idsInSnapshot[k] {
  2002  			// Delete metadata for IDs that are no longer reachable from files
  2003  			// in the snapshot.
  2004  			continue
  2005  		}
  2006  		invalidateMetadata := idsToInvalidate[k]
  2007  		// Mark invalidated metadata rather than deleting it outright.
  2008  		result.metadata[k] = &KnownMetadata{
  2009  			Metadata:   v.Metadata,
  2010  			Valid:      v.Valid && !invalidateMetadata,
  2011  			ShouldLoad: v.ShouldLoad || invalidateMetadata,
  2012  		}
  2013  	}
  2014  
  2015  	// Copy the set of initially loaded packages.
  2016  	for id, pkgPath := range s.workspacePackages {
  2017  		// Packages with the id "command-line-arguments" are generated by the
  2018  		// go command when the user is outside of GOPATH and outside of a
  2019  		// module. Do not cache them as workspace packages for longer than
  2020  		// necessary.
  2021  		if source.IsCommandLineArguments(string(id)) {
  2022  			if invalidateMetadata, ok := idsToInvalidate[id]; invalidateMetadata && ok {
  2023  				continue
  2024  			}
  2025  		}
  2026  
  2027  		// If all the files we know about in a package have been deleted,
  2028  		// the package is gone and we should no longer try to load it.
  2029  		if m := s.metadata[id]; m != nil {
  2030  			hasFiles := false
  2031  			for _, uri := range s.metadata[id].GoFiles {
  2032  				// For internal tests, we need _test files, not just the normal
  2033  				// ones. External tests only have _test files, but we can check
  2034  				// them anyway.
  2035  				if m.ForTest != "" && !strings.HasSuffix(string(uri), "_test.go") {
  2036  					continue
  2037  				}
  2038  				if _, ok := result.files[uri]; ok {
  2039  					hasFiles = true
  2040  					break
  2041  				}
  2042  			}
  2043  			if !hasFiles {
  2044  				continue
  2045  			}
  2046  		}
  2047  
  2048  		// If the package name of a file in the package has changed, it's
  2049  		// possible that the package ID may no longer exist. Delete it from
  2050  		// the set of workspace packages, on the assumption that we will add it
  2051  		// back when the relevant files are reloaded.
  2052  		if _, ok := changedPkgNames[id]; ok {
  2053  			continue
  2054  		}
  2055  
  2056  		result.workspacePackages[id] = pkgPath
  2057  	}
  2058  
  2059  	// Inherit all of the go.mod-related handles.
  2060  	for _, v := range result.modTidyHandles {
  2061  		newGen.Inherit(v.handle)
  2062  	}
  2063  	for _, v := range result.modWhyHandles {
  2064  		newGen.Inherit(v.handle)
  2065  	}
  2066  	for _, v := range result.parseModHandles {
  2067  		newGen.Inherit(v.handle)
  2068  	}
  2069  	for _, v := range result.parseWorkHandles {
  2070  		newGen.Inherit(v.handle)
  2071  	}
  2072  	// Don't bother copying the importedBy graph,
  2073  	// as it changes each time we update metadata.
  2074  
  2075  	// If the snapshot's workspace mode has changed, the packages loaded using
  2076  	// the previous mode are no longer relevant, so clear them out.
  2077  	if workspaceModeChanged {
  2078  		result.workspacePackages = map[PackageID]PackagePath{}
  2079  	}
  2080  
  2081  	// The snapshot may need to be reinitialized.
  2082  	if workspaceReload || vendorChanged {
  2083  		if workspaceChanged || result.initializedErr != nil {
  2084  			result.initializeOnce = &sync.Once{}
  2085  		}
  2086  	}
  2087  	return result
  2088  }
  2089  
  2090  // guessPackageIDsForURI returns all packages related to uri. If we haven't
  2091  // seen this URI before, we guess based on files in the same directory. This
  2092  // is of course incorrect in build systems where packages are not organized by
  2093  // directory.
  2094  func guessPackageIDsForURI(uri span.URI, known map[span.URI][]PackageID) []PackageID {
  2095  	packages := known[uri]
  2096  	if len(packages) > 0 {
  2097  		// We've seen this file before.
  2098  		return packages
  2099  	}
  2100  	// This is a file we don't yet know about. Guess relevant packages by
  2101  	// considering files in the same directory.
  2102  
  2103  	// Cache of FileInfo to avoid unnecessary stats for multiple files in the
  2104  	// same directory.
  2105  	stats := make(map[string]struct {
  2106  		os.FileInfo
  2107  		error
  2108  	})
  2109  	getInfo := func(dir string) (os.FileInfo, error) {
  2110  		if res, ok := stats[dir]; ok {
  2111  			return res.FileInfo, res.error
  2112  		}
  2113  		fi, err := os.Stat(dir)
  2114  		stats[dir] = struct {
  2115  			os.FileInfo
  2116  			error
  2117  		}{fi, err}
  2118  		return fi, err
  2119  	}
  2120  	dir := filepath.Dir(uri.Filename())
  2121  	fi, err := getInfo(dir)
  2122  	if err != nil {
  2123  		return nil
  2124  	}
  2125  
  2126  	// Aggregate all possibly relevant package IDs.
  2127  	var found []PackageID
  2128  	for knownURI, ids := range known {
  2129  		knownDir := filepath.Dir(knownURI.Filename())
  2130  		knownFI, err := getInfo(knownDir)
  2131  		if err != nil {
  2132  			continue
  2133  		}
  2134  		if os.SameFile(fi, knownFI) {
  2135  			found = append(found, ids...)
  2136  		}
  2137  	}
  2138  	return found
  2139  }
  2140  
  2141  // fileWasSaved reports whether the FileHandle passed in has been saved. It
  2142  // accomplishes this by checking to see if the original and current FileHandles
  2143  // are both overlays, and if the current FileHandle is saved while the original
  2144  // FileHandle was not saved.
  2145  func fileWasSaved(originalFH, currentFH source.FileHandle) bool {
  2146  	c, ok := currentFH.(*overlay)
  2147  	if !ok || c == nil {
  2148  		return true
  2149  	}
  2150  	o, ok := originalFH.(*overlay)
  2151  	if !ok || o == nil {
  2152  		return c.saved
  2153  	}
  2154  	return !o.saved && c.saved
  2155  }
  2156  
  2157  // shouldInvalidateMetadata reparses the full file's AST to determine
  2158  // if the file requires a metadata reload.
  2159  func (s *snapshot) shouldInvalidateMetadata(ctx context.Context, newSnapshot *snapshot, originalFH, currentFH source.FileHandle) (invalidate, pkgNameChanged, importDeleted bool) {
  2160  	if originalFH == nil {
  2161  		return true, false, false
  2162  	}
  2163  	// If the file hasn't changed, there's no need to reload.
  2164  	if originalFH.FileIdentity() == currentFH.FileIdentity() {
  2165  		return false, false, false
  2166  	}
  2167  	// Get the original and current parsed files in order to check package name
  2168  	// and imports. Use the new snapshot to parse to avoid modifying the
  2169  	// current snapshot.
  2170  	original, originalErr := newSnapshot.ParseGo(ctx, originalFH, source.ParseFull)
  2171  	current, currentErr := newSnapshot.ParseGo(ctx, currentFH, source.ParseFull)
  2172  	if originalErr != nil || currentErr != nil {
  2173  		return (originalErr == nil) != (currentErr == nil), false, (currentErr != nil) // we don't know if an import was deleted
  2174  	}
  2175  	// Check if the package's metadata has changed. The cases handled are:
  2176  	//    1. A package's name has changed
  2177  	//    2. A file's imports have changed
  2178  	if original.File.Name.Name != current.File.Name.Name {
  2179  		invalidate = true
  2180  		pkgNameChanged = true
  2181  	}
  2182  	origImportSet := make(map[string]struct{})
  2183  	for _, importSpec := range original.File.Imports {
  2184  		origImportSet[importSpec.Path.Value] = struct{}{}
  2185  	}
  2186  	curImportSet := make(map[string]struct{})
  2187  	for _, importSpec := range current.File.Imports {
  2188  		curImportSet[importSpec.Path.Value] = struct{}{}
  2189  	}
  2190  	// If any of the current imports were not in the original imports.
  2191  	for path := range curImportSet {
  2192  		if _, ok := origImportSet[path]; ok {
  2193  			delete(origImportSet, path)
  2194  			continue
  2195  		}
  2196  		// If the import path is obviously not valid, we can skip reloading
  2197  		// metadata. For now, valid means properly quoted and without a
  2198  		// terminal slash.
  2199  		if isBadImportPath(path) {
  2200  			continue
  2201  		}
  2202  		invalidate = true
  2203  	}
  2204  
  2205  	for path := range origImportSet {
  2206  		if !isBadImportPath(path) {
  2207  			invalidate = true
  2208  			importDeleted = true
  2209  		}
  2210  	}
  2211  
  2212  	if !invalidate {
  2213  		invalidate = magicCommentsChanged(original.File, current.File)
  2214  	}
  2215  	return invalidate, pkgNameChanged, importDeleted
  2216  }
  2217  
  2218  func magicCommentsChanged(original *ast.File, current *ast.File) bool {
  2219  	oldComments := extractMagicComments(original)
  2220  	newComments := extractMagicComments(current)
  2221  	if len(oldComments) != len(newComments) {
  2222  		return true
  2223  	}
  2224  	for i := range oldComments {
  2225  		if oldComments[i] != newComments[i] {
  2226  			return true
  2227  		}
  2228  	}
  2229  	return false
  2230  }
  2231  
  2232  func isBadImportPath(path string) bool {
  2233  	path, err := strconv.Unquote(path)
  2234  	if err != nil {
  2235  		return true
  2236  	}
  2237  	if path == "" {
  2238  		return true
  2239  	}
  2240  	if path[len(path)-1] == '/' {
  2241  		return true
  2242  	}
  2243  	return false
  2244  }
  2245  
  2246  var buildConstraintOrEmbedRe = regexp.MustCompile(`^//(go:embed|go:build|\s*\+build).*`)
  2247  
  2248  // extractMagicComments finds magic comments that affect metadata in f.
  2249  func extractMagicComments(f *ast.File) []string {
  2250  	var results []string
  2251  	for _, cg := range f.Comments {
  2252  		for _, c := range cg.List {
  2253  			if buildConstraintOrEmbedRe.MatchString(c.Text) {
  2254  				results = append(results, c.Text)
  2255  			}
  2256  		}
  2257  	}
  2258  	return results
  2259  }
  2260  
  2261  func (s *snapshot) BuiltinFile(ctx context.Context) (*source.ParsedGoFile, error) {
  2262  	s.AwaitInitialized(ctx)
  2263  
  2264  	s.mu.Lock()
  2265  	builtin := s.builtin
  2266  	s.mu.Unlock()
  2267  
  2268  	if builtin == "" {
  2269  		return nil, errors.Errorf("no builtin package for view %s", s.view.name)
  2270  	}
  2271  
  2272  	fh, err := s.GetFile(ctx, builtin)
  2273  	if err != nil {
  2274  		return nil, err
  2275  	}
  2276  	return s.ParseGo(ctx, fh, source.ParseFull)
  2277  }
  2278  
  2279  func (s *snapshot) IsBuiltin(ctx context.Context, uri span.URI) bool {
  2280  	s.mu.Lock()
  2281  	defer s.mu.Unlock()
  2282  	// We should always get the builtin URI in a canonical form, so use simple
  2283  	// string comparison here. span.CompareURI is too expensive.
  2284  	return uri == s.builtin
  2285  }
  2286  
  2287  func (s *snapshot) setBuiltin(path string) {
  2288  	s.mu.Lock()
  2289  	defer s.mu.Unlock()
  2290  
  2291  	s.builtin = span.URIFromPath(path)
  2292  }
  2293  
  2294  // BuildGoplsMod generates a go.mod file for all modules in the workspace. It
  2295  // bypasses any existing gopls.mod.
  2296  func (s *snapshot) BuildGoplsMod(ctx context.Context) (*modfile.File, error) {
  2297  	allModules, err := findModules(s.view.folder, pathExcludedByFilterFunc(s.view.rootURI.Filename(), s.view.gomodcache, s.View().Options()), 0)
  2298  	if err != nil {
  2299  		return nil, err
  2300  	}
  2301  	return buildWorkspaceModFile(ctx, allModules, s)
  2302  }
  2303  
  2304  // TODO(rfindley): move this to workspacemodule.go
  2305  func buildWorkspaceModFile(ctx context.Context, modFiles map[span.URI]struct{}, fs source.FileSource) (*modfile.File, error) {
  2306  	file := &modfile.File{}
  2307  	file.AddModuleStmt("gopls-workspace")
  2308  	// Track the highest Go version, to be set on the workspace module.
  2309  	// Fall back to 1.12 -- old versions insist on having some version.
  2310  	goVersion := "1.12"
  2311  
  2312  	paths := map[string]span.URI{}
  2313  	excludes := map[string][]string{}
  2314  	var sortedModURIs []span.URI
  2315  	for uri := range modFiles {
  2316  		sortedModURIs = append(sortedModURIs, uri)
  2317  	}
  2318  	sort.Slice(sortedModURIs, func(i, j int) bool {
  2319  		return sortedModURIs[i] < sortedModURIs[j]
  2320  	})
  2321  	for _, modURI := range sortedModURIs {
  2322  		fh, err := fs.GetFile(ctx, modURI)
  2323  		if err != nil {
  2324  			return nil, err
  2325  		}
  2326  		content, err := fh.Read()
  2327  		if err != nil {
  2328  			return nil, err
  2329  		}
  2330  		parsed, err := modfile.Parse(fh.URI().Filename(), content, nil)
  2331  		if err != nil {
  2332  			return nil, err
  2333  		}
  2334  		if file == nil || parsed.Module == nil {
  2335  			return nil, fmt.Errorf("no module declaration for %s", modURI)
  2336  		}
  2337  		// Prepend "v" to go versions to make them valid semver.
  2338  		if parsed.Go != nil && semver.Compare("v"+goVersion, "v"+parsed.Go.Version) < 0 {
  2339  			goVersion = parsed.Go.Version
  2340  		}
  2341  		path := parsed.Module.Mod.Path
  2342  		if _, ok := paths[path]; ok {
  2343  			return nil, fmt.Errorf("found module %q twice in the workspace", path)
  2344  		}
  2345  		paths[path] = modURI
  2346  		// If the module's path includes a major version, we expect it to have
  2347  		// a matching major version.
  2348  		_, majorVersion, _ := module.SplitPathVersion(path)
  2349  		if majorVersion == "" {
  2350  			majorVersion = "/v0"
  2351  		}
  2352  		majorVersion = strings.TrimLeft(majorVersion, "/.") // handle gopkg.in versions
  2353  		file.AddNewRequire(path, source.WorkspaceModuleVersion(majorVersion), false)
  2354  		if err := file.AddReplace(path, "", dirURI(modURI).Filename(), ""); err != nil {
  2355  			return nil, err
  2356  		}
  2357  		for _, exclude := range parsed.Exclude {
  2358  			excludes[exclude.Mod.Path] = append(excludes[exclude.Mod.Path], exclude.Mod.Version)
  2359  		}
  2360  	}
  2361  	if goVersion != "" {
  2362  		file.AddGoStmt(goVersion)
  2363  	}
  2364  	// Go back through all of the modules to handle any of their replace
  2365  	// statements.
  2366  	for _, modURI := range sortedModURIs {
  2367  		fh, err := fs.GetFile(ctx, modURI)
  2368  		if err != nil {
  2369  			return nil, err
  2370  		}
  2371  		content, err := fh.Read()
  2372  		if err != nil {
  2373  			return nil, err
  2374  		}
  2375  		parsed, err := modfile.Parse(fh.URI().Filename(), content, nil)
  2376  		if err != nil {
  2377  			return nil, err
  2378  		}
  2379  		// If any of the workspace modules have replace directives, they need
  2380  		// to be reflected in the workspace module.
  2381  		for _, rep := range parsed.Replace {
  2382  			// Don't replace any modules that are in our workspace--we should
  2383  			// always use the version in the workspace.
  2384  			if _, ok := paths[rep.Old.Path]; ok {
  2385  				continue
  2386  			}
  2387  			newPath := rep.New.Path
  2388  			newVersion := rep.New.Version
  2389  			// If a replace points to a module in the workspace, make sure we
  2390  			// direct it to version of the module in the workspace.
  2391  			if m, ok := paths[rep.New.Path]; ok {
  2392  				newPath = dirURI(m).Filename()
  2393  				newVersion = ""
  2394  			} else if rep.New.Version == "" && !filepath.IsAbs(rep.New.Path) {
  2395  				// Make any relative paths absolute.
  2396  				newPath = filepath.Join(dirURI(modURI).Filename(), rep.New.Path)
  2397  			}
  2398  			if err := file.AddReplace(rep.Old.Path, rep.Old.Version, newPath, newVersion); err != nil {
  2399  				return nil, err
  2400  			}
  2401  		}
  2402  	}
  2403  	for path, versions := range excludes {
  2404  		for _, version := range versions {
  2405  			file.AddExclude(path, version)
  2406  		}
  2407  	}
  2408  	file.SortBlocks()
  2409  	return file, nil
  2410  }
  2411  
  2412  func buildWorkspaceSumFile(ctx context.Context, modFiles map[span.URI]struct{}, fs source.FileSource) ([]byte, error) {
  2413  	allSums := map[module.Version][]string{}
  2414  	for modURI := range modFiles {
  2415  		// TODO(rfindley): factor out this pattern into a uripath package.
  2416  		sumURI := span.URIFromPath(filepath.Join(filepath.Dir(modURI.Filename()), "go.sum"))
  2417  		fh, err := fs.GetFile(ctx, sumURI)
  2418  		if err != nil {
  2419  			continue
  2420  		}
  2421  		data, err := fh.Read()
  2422  		if os.IsNotExist(err) {
  2423  			continue
  2424  		}
  2425  		if err != nil {
  2426  			return nil, errors.Errorf("reading go sum: %w", err)
  2427  		}
  2428  		if err := readGoSum(allSums, sumURI.Filename(), data); err != nil {
  2429  			return nil, err
  2430  		}
  2431  	}
  2432  	// This logic to write go.sum is copied (with minor modifications) from
  2433  	// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/modfetch/fetch.go;l=631;drc=762eda346a9f4062feaa8a9fc0d17d72b11586f0
  2434  	var mods []module.Version
  2435  	for m := range allSums {
  2436  		mods = append(mods, m)
  2437  	}
  2438  	module.Sort(mods)
  2439  
  2440  	var buf bytes.Buffer
  2441  	for _, m := range mods {
  2442  		list := allSums[m]
  2443  		sort.Strings(list)
  2444  		// Note (rfindley): here we add all sum lines without verification, because
  2445  		// the assumption is that if they come from a go.sum file, they are
  2446  		// trusted.
  2447  		for _, h := range list {
  2448  			fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
  2449  		}
  2450  	}
  2451  	return buf.Bytes(), nil
  2452  }
  2453  
  2454  // readGoSum is copied (with minor modifications) from
  2455  // https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/modfetch/fetch.go;l=398;drc=762eda346a9f4062feaa8a9fc0d17d72b11586f0
  2456  func readGoSum(dst map[module.Version][]string, file string, data []byte) error {
  2457  	lineno := 0
  2458  	for len(data) > 0 {
  2459  		var line []byte
  2460  		lineno++
  2461  		i := bytes.IndexByte(data, '\n')
  2462  		if i < 0 {
  2463  			line, data = data, nil
  2464  		} else {
  2465  			line, data = data[:i], data[i+1:]
  2466  		}
  2467  		f := strings.Fields(string(line))
  2468  		if len(f) == 0 {
  2469  			// blank line; skip it
  2470  			continue
  2471  		}
  2472  		if len(f) != 3 {
  2473  			return fmt.Errorf("malformed go.sum:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
  2474  		}
  2475  		mod := module.Version{Path: f[0], Version: f[1]}
  2476  		dst[mod] = append(dst[mod], f[2])
  2477  	}
  2478  	return nil
  2479  }