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