github.com/jd-ly/tools@v0.5.7/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  	"os"
    16  	"path/filepath"
    17  	"sort"
    18  	"strconv"
    19  	"strings"
    20  	"sync"
    21  
    22  	"golang.org/x/mod/modfile"
    23  	"golang.org/x/mod/module"
    24  	"github.com/jd-ly/tools/go/analysis"
    25  	"github.com/jd-ly/tools/go/packages"
    26  	"github.com/jd-ly/tools/internal/event"
    27  	"github.com/jd-ly/tools/internal/gocommand"
    28  	"github.com/jd-ly/tools/internal/lsp/debug/tag"
    29  	"github.com/jd-ly/tools/internal/lsp/source"
    30  	"github.com/jd-ly/tools/internal/memoize"
    31  	"github.com/jd-ly/tools/internal/packagesinternal"
    32  	"github.com/jd-ly/tools/internal/span"
    33  	"github.com/jd-ly/tools/internal/typesinternal"
    34  	errors "golang.org/x/xerrors"
    35  )
    36  
    37  type snapshot struct {
    38  	memoize.Arg // allow as a memoize.Function arg
    39  
    40  	id   uint64
    41  	view *View
    42  
    43  	cancel        func()
    44  	backgroundCtx context.Context
    45  
    46  	// the cache generation that contains the data for this snapshot.
    47  	generation *memoize.Generation
    48  
    49  	// builtin pins the AST and package for builtin.go in memory.
    50  	builtin *builtinPackageHandle
    51  
    52  	// The snapshot's initialization state is controlled by the fields below.
    53  	//
    54  	// initializeOnce guards snapshot initialization. Each snapshot is
    55  	// initialized at most once: reinitialization is triggered on later snapshots
    56  	// by invalidating this field.
    57  	initializeOnce *sync.Once
    58  	// initializedErr holds the last error resulting from initialization. If
    59  	// initialization fails, we only retry when the the workspace modules change,
    60  	// to avoid too many go/packages calls.
    61  	initializedErr error
    62  
    63  	// mu guards all of the maps in the snapshot.
    64  	mu sync.Mutex
    65  
    66  	// ids maps file URIs to package IDs.
    67  	// It may be invalidated on calls to go/packages.
    68  	ids map[span.URI][]packageID
    69  
    70  	// metadata maps file IDs to their associated metadata.
    71  	// It may invalidated on calls to go/packages.
    72  	metadata map[packageID]*metadata
    73  
    74  	// importedBy maps package IDs to the list of packages that import them.
    75  	importedBy map[packageID][]packageID
    76  
    77  	// files maps file URIs to their corresponding FileHandles.
    78  	// It may invalidated when a file's content changes.
    79  	files map[span.URI]source.VersionedFileHandle
    80  
    81  	// goFiles maps a parseKey to its parseGoHandle.
    82  	goFiles map[parseKey]*parseGoHandle
    83  
    84  	// packages maps a packageKey to a set of packageHandles to which that file belongs.
    85  	// It may be invalidated when a file's content changes.
    86  	packages map[packageKey]*packageHandle
    87  
    88  	// actions maps an actionkey to its actionHandle.
    89  	actions map[actionKey]*actionHandle
    90  
    91  	// workspacePackages contains the workspace's packages, which are loaded
    92  	// when the view is created.
    93  	workspacePackages map[packageID]packagePath
    94  
    95  	// unloadableFiles keeps track of files that we've failed to load.
    96  	unloadableFiles map[span.URI]struct{}
    97  
    98  	// parseModHandles keeps track of any ParseModHandles for the snapshot.
    99  	// The handles need not refer to only the view's go.mod file.
   100  	parseModHandles map[span.URI]*parseModHandle
   101  
   102  	// Preserve go.mod-related handles to avoid garbage-collecting the results
   103  	// of various calls to the go command. The handles need not refer to only
   104  	// the view's go.mod file.
   105  	modTidyHandles    map[span.URI]*modTidyHandle
   106  	modUpgradeHandles map[span.URI]*modUpgradeHandle
   107  	modWhyHandles     map[span.URI]*modWhyHandle
   108  
   109  	workspace          *workspace
   110  	workspaceDirHandle *memoize.Handle
   111  }
   112  
   113  type packageKey struct {
   114  	mode source.ParseMode
   115  	id   packageID
   116  }
   117  
   118  type actionKey struct {
   119  	pkg      packageKey
   120  	analyzer *analysis.Analyzer
   121  }
   122  
   123  func (s *snapshot) ID() uint64 {
   124  	return s.id
   125  }
   126  
   127  func (s *snapshot) View() source.View {
   128  	return s.view
   129  }
   130  
   131  func (s *snapshot) BackgroundContext() context.Context {
   132  	return s.backgroundCtx
   133  }
   134  
   135  func (s *snapshot) FileSet() *token.FileSet {
   136  	return s.view.session.cache.fset
   137  }
   138  
   139  func (s *snapshot) ModFiles() []span.URI {
   140  	var uris []span.URI
   141  	for modURI := range s.workspace.getActiveModFiles() {
   142  		uris = append(uris, modURI)
   143  	}
   144  	return uris
   145  }
   146  
   147  func (s *snapshot) ValidBuildConfiguration() bool {
   148  	return validBuildConfiguration(s.view.rootURI, &s.view.workspaceInformation, s.workspace.getActiveModFiles())
   149  }
   150  
   151  // workspaceMode describes the way in which the snapshot's workspace should
   152  // be loaded.
   153  func (s *snapshot) workspaceMode() workspaceMode {
   154  	var mode workspaceMode
   155  
   156  	// If the view has an invalid configuration, don't build the workspace
   157  	// module.
   158  	validBuildConfiguration := s.ValidBuildConfiguration()
   159  	if !validBuildConfiguration {
   160  		return mode
   161  	}
   162  	// If the view is not in a module and contains no modules, but still has a
   163  	// valid workspace configuration, do not create the workspace module.
   164  	// It could be using GOPATH or a different build system entirely.
   165  	if len(s.workspace.getActiveModFiles()) == 0 && validBuildConfiguration {
   166  		return mode
   167  	}
   168  	mode |= moduleMode
   169  	options := s.view.Options()
   170  	// The -modfile flag is available for Go versions >= 1.14.
   171  	if options.TempModfile && s.view.workspaceInformation.goversion >= 14 {
   172  		mode |= tempModfile
   173  	}
   174  	// If the user is intentionally limiting their workspace scope, don't
   175  	// enable multi-module workspace mode.
   176  	// TODO(rstambler): This should only change the calculation of the root,
   177  	// not the mode.
   178  	if !options.ExpandWorkspaceToModule {
   179  		return mode
   180  	}
   181  	// The workspace module has been disabled by the user.
   182  	if !options.ExperimentalWorkspaceModule {
   183  		return mode
   184  	}
   185  	mode |= usesWorkspaceModule
   186  	return mode
   187  }
   188  
   189  // config returns the configuration used for the snapshot's interaction with
   190  // the go/packages API. It uses the given working directory.
   191  //
   192  // TODO(rstambler): go/packages requires that we do not provide overlays for
   193  // multiple modules in on config, so buildOverlay needs to filter overlays by
   194  // module.
   195  func (s *snapshot) config(ctx context.Context, inv *gocommand.Invocation) *packages.Config {
   196  	s.view.optionsMu.Lock()
   197  	verboseOutput := s.view.options.VerboseOutput
   198  	s.view.optionsMu.Unlock()
   199  
   200  	// Forcibly disable GOPACKAGESDRIVER. It's incompatible with the
   201  	// packagesinternal APIs we use, and we really only support the go command
   202  	// anyway.
   203  	env := append(append([]string{}, inv.Env...), "GOPACKAGESDRIVER=off")
   204  	cfg := &packages.Config{
   205  		Context:    ctx,
   206  		Dir:        inv.WorkingDir,
   207  		Env:        env,
   208  		BuildFlags: inv.BuildFlags,
   209  		Mode: packages.NeedName |
   210  			packages.NeedFiles |
   211  			packages.NeedCompiledGoFiles |
   212  			packages.NeedImports |
   213  			packages.NeedDeps |
   214  			packages.NeedTypesSizes |
   215  			packages.NeedModule,
   216  		Fset:    s.view.session.cache.fset,
   217  		Overlay: s.buildOverlay(),
   218  		ParseFile: func(*token.FileSet, string, []byte) (*ast.File, error) {
   219  			panic("go/packages must not be used to parse files")
   220  		},
   221  		Logf: func(format string, args ...interface{}) {
   222  			if verboseOutput {
   223  				event.Log(ctx, fmt.Sprintf(format, args...))
   224  			}
   225  		},
   226  		Tests: true,
   227  	}
   228  	packagesinternal.SetModFile(cfg, inv.ModFile)
   229  	packagesinternal.SetModFlag(cfg, inv.ModFlag)
   230  	// We want to type check cgo code if go/types supports it.
   231  	if typesinternal.SetUsesCgo(&types.Config{}) {
   232  		cfg.Mode |= packages.LoadMode(packagesinternal.TypecheckCgo)
   233  	}
   234  	packagesinternal.SetGoCmdRunner(cfg, s.view.session.gocmdRunner)
   235  	return cfg
   236  }
   237  
   238  func (s *snapshot) RunGoCommandDirect(ctx context.Context, mode source.InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error) {
   239  	_, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  	defer cleanup()
   244  
   245  	return s.view.session.gocmdRunner.Run(ctx, *inv)
   246  }
   247  
   248  func (s *snapshot) RunGoCommandPiped(ctx context.Context, mode source.InvocationFlags, inv *gocommand.Invocation, stdout, stderr io.Writer) error {
   249  	_, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
   250  	if err != nil {
   251  		return err
   252  	}
   253  	defer cleanup()
   254  	return s.view.session.gocmdRunner.RunPiped(ctx, *inv, stdout, stderr)
   255  }
   256  
   257  func (s *snapshot) goCommandInvocation(ctx context.Context, flags source.InvocationFlags, inv *gocommand.Invocation) (tmpURI span.URI, updatedInv *gocommand.Invocation, cleanup func(), err error) {
   258  	s.view.optionsMu.Lock()
   259  	allowModfileModificationOption := s.view.options.AllowModfileModifications
   260  	allowNetworkOption := s.view.options.AllowImplicitNetworkAccess
   261  	inv.Env = append(append(append(os.Environ(), s.view.options.EnvSlice()...), inv.Env...), "GO111MODULE="+s.view.effectiveGo111Module)
   262  	inv.BuildFlags = append([]string{}, s.view.options.BuildFlags...)
   263  	s.view.optionsMu.Unlock()
   264  	cleanup = func() {} // fallback
   265  
   266  	// All logic below is for module mode.
   267  	if s.workspaceMode()&moduleMode == 0 {
   268  		return "", inv, cleanup, nil
   269  	}
   270  
   271  	mode, allowNetwork := flags.Mode(), flags.AllowNetwork()
   272  	if !allowNetwork && !allowNetworkOption {
   273  		inv.Env = append(inv.Env, "GOPROXY=off")
   274  	}
   275  
   276  	var modURI span.URI
   277  	// Select the module context to use.
   278  	// If we're type checking, we need to use the workspace context, meaning
   279  	// the main (workspace) module. Otherwise, we should use the module for
   280  	// the passed-in working dir.
   281  	if mode == source.LoadWorkspace {
   282  		if s.workspaceMode()&usesWorkspaceModule == 0 {
   283  			for m := range s.workspace.getActiveModFiles() { // range to access the only element
   284  				modURI = m
   285  			}
   286  		} else {
   287  			var tmpDir span.URI
   288  			var err error
   289  			tmpDir, err = s.getWorkspaceDir(ctx)
   290  			if err != nil {
   291  				return "", nil, cleanup, err
   292  			}
   293  			inv.WorkingDir = tmpDir.Filename()
   294  			modURI = span.URIFromPath(filepath.Join(tmpDir.Filename(), "go.mod"))
   295  		}
   296  	} else {
   297  		modURI = s.GoModForFile(ctx, span.URIFromPath(inv.WorkingDir))
   298  	}
   299  
   300  	var modContent []byte
   301  	if modURI != "" {
   302  		modFH, err := s.GetFile(ctx, modURI)
   303  		if err != nil {
   304  			return "", nil, cleanup, err
   305  		}
   306  		modContent, err = modFH.Read()
   307  		if err != nil {
   308  			return "", nil, cleanup, err
   309  		}
   310  	}
   311  
   312  	vendorEnabled, err := s.vendorEnabled(ctx, modURI, modContent)
   313  	if err != nil {
   314  		return "", nil, cleanup, err
   315  	}
   316  
   317  	mutableModFlag := ""
   318  	if s.view.goversion >= 16 {
   319  		mutableModFlag = "mod"
   320  	}
   321  
   322  	switch mode {
   323  	case source.LoadWorkspace, source.Normal:
   324  		if vendorEnabled {
   325  			inv.ModFlag = "vendor"
   326  		} else if s.workspaceMode()&usesWorkspaceModule == 0 && !allowModfileModificationOption {
   327  			inv.ModFlag = "readonly"
   328  		} else {
   329  			// Temporarily allow updates for multi-module workspace mode:
   330  			// it doesn't create a go.sum at all. golang/go#42509.
   331  			inv.ModFlag = mutableModFlag
   332  		}
   333  	case source.UpdateUserModFile, source.WriteTemporaryModFile:
   334  		inv.ModFlag = mutableModFlag
   335  	}
   336  
   337  	wantTempMod := mode != source.UpdateUserModFile
   338  	needTempMod := mode == source.WriteTemporaryModFile
   339  	tempMod := wantTempMod && s.workspaceMode()&tempModfile != 0
   340  	if needTempMod && !tempMod {
   341  		return "", nil, cleanup, source.ErrTmpModfileUnsupported
   342  	}
   343  
   344  	if tempMod {
   345  		if modURI == "" {
   346  			return "", nil, cleanup, fmt.Errorf("no go.mod file found in %s", inv.WorkingDir)
   347  		}
   348  		modFH, err := s.GetFile(ctx, modURI)
   349  		if err != nil {
   350  			return "", nil, cleanup, err
   351  		}
   352  		// Use the go.sum if it happens to be available.
   353  		gosum := s.goSum(ctx, modURI)
   354  		tmpURI, cleanup, err = tempModFile(modFH, gosum)
   355  		if err != nil {
   356  			return "", nil, cleanup, err
   357  		}
   358  		inv.ModFile = tmpURI.Filename()
   359  	}
   360  
   361  	return tmpURI, inv, cleanup, nil
   362  }
   363  
   364  func (s *snapshot) buildOverlay() map[string][]byte {
   365  	s.mu.Lock()
   366  	defer s.mu.Unlock()
   367  
   368  	overlays := make(map[string][]byte)
   369  	for uri, fh := range s.files {
   370  		overlay, ok := fh.(*overlay)
   371  		if !ok {
   372  			continue
   373  		}
   374  		if overlay.saved {
   375  			continue
   376  		}
   377  		// TODO(rstambler): Make sure not to send overlays outside of the current view.
   378  		overlays[uri.Filename()] = overlay.text
   379  	}
   380  	return overlays
   381  }
   382  
   383  func hashUnsavedOverlays(files map[span.URI]source.VersionedFileHandle) string {
   384  	var unsaved []string
   385  	for uri, fh := range files {
   386  		if overlay, ok := fh.(*overlay); ok && !overlay.saved {
   387  			unsaved = append(unsaved, uri.Filename())
   388  		}
   389  	}
   390  	sort.Strings(unsaved)
   391  	return hashContents([]byte(strings.Join(unsaved, "")))
   392  }
   393  
   394  func (s *snapshot) PackagesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode) ([]source.Package, error) {
   395  	ctx = event.Label(ctx, tag.URI.Of(uri))
   396  
   397  	phs, err := s.packageHandlesForFile(ctx, uri, mode)
   398  	if err != nil {
   399  		return nil, err
   400  	}
   401  	var pkgs []source.Package
   402  	for _, ph := range phs {
   403  		pkg, err := ph.check(ctx, s)
   404  		if err != nil {
   405  			return nil, err
   406  		}
   407  		pkgs = append(pkgs, pkg)
   408  	}
   409  	return pkgs, nil
   410  }
   411  
   412  func (s *snapshot) PackageForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode, pkgPolicy source.PackageFilter) (source.Package, error) {
   413  	ctx = event.Label(ctx, tag.URI.Of(uri))
   414  
   415  	phs, err := s.packageHandlesForFile(ctx, uri, mode)
   416  	if err != nil {
   417  		return nil, err
   418  	}
   419  
   420  	if len(phs) < 1 {
   421  		return nil, errors.Errorf("no packages")
   422  	}
   423  
   424  	ph := phs[0]
   425  	for _, handle := range phs[1:] {
   426  		switch pkgPolicy {
   427  		case source.WidestPackage:
   428  			if ph == nil || len(handle.CompiledGoFiles()) > len(ph.CompiledGoFiles()) {
   429  				ph = handle
   430  			}
   431  		case source.NarrowestPackage:
   432  			if ph == nil || len(handle.CompiledGoFiles()) < len(ph.CompiledGoFiles()) {
   433  				ph = handle
   434  			}
   435  		}
   436  	}
   437  	if ph == nil {
   438  		return nil, errors.Errorf("no packages in input")
   439  	}
   440  
   441  	return ph.check(ctx, s)
   442  }
   443  
   444  func (s *snapshot) packageHandlesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode) ([]*packageHandle, error) {
   445  	// Check if we should reload metadata for the file. We don't invalidate IDs
   446  	// (though we should), so the IDs will be a better source of truth than the
   447  	// metadata. If there are no IDs for the file, then we should also reload.
   448  	fh, err := s.GetFile(ctx, uri)
   449  	if err != nil {
   450  		return nil, err
   451  	}
   452  	if fh.Kind() != source.Go {
   453  		return nil, fmt.Errorf("no packages for non-Go file %s", uri)
   454  	}
   455  	ids := s.getIDsForURI(uri)
   456  	reload := len(ids) == 0
   457  	for _, id := range ids {
   458  		// Reload package metadata if any of the metadata has missing
   459  		// dependencies, in case something has changed since the last time we
   460  		// reloaded it.
   461  		if m := s.getMetadata(id); m == nil {
   462  			reload = true
   463  			break
   464  		}
   465  		// TODO(golang/go#36918): Previously, we would reload any package with
   466  		// missing dependencies. This is expensive and results in too many
   467  		// calls to packages.Load. Determine what we should do instead.
   468  	}
   469  	if reload {
   470  		if err := s.load(ctx, false, fileURI(uri)); err != nil {
   471  			return nil, err
   472  		}
   473  	}
   474  	// Get the list of IDs from the snapshot again, in case it has changed.
   475  	var phs []*packageHandle
   476  	for _, id := range s.getIDsForURI(uri) {
   477  		var parseModes []source.ParseMode
   478  		switch mode {
   479  		case source.TypecheckAll:
   480  			if s.workspaceParseMode(id) == source.ParseFull {
   481  				parseModes = []source.ParseMode{source.ParseFull}
   482  			} else {
   483  				parseModes = []source.ParseMode{source.ParseExported, source.ParseFull}
   484  			}
   485  		case source.TypecheckFull:
   486  			parseModes = []source.ParseMode{source.ParseFull}
   487  		case source.TypecheckWorkspace:
   488  			parseModes = []source.ParseMode{s.workspaceParseMode(id)}
   489  		}
   490  
   491  		for _, parseMode := range parseModes {
   492  			ph, err := s.buildPackageHandle(ctx, id, parseMode)
   493  			if err != nil {
   494  				return nil, err
   495  			}
   496  			phs = append(phs, ph)
   497  		}
   498  	}
   499  
   500  	return phs, nil
   501  }
   502  
   503  func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]source.Package, error) {
   504  	if err := s.awaitLoaded(ctx); err != nil {
   505  		return nil, err
   506  	}
   507  	ids := make(map[packageID]struct{})
   508  	s.transitiveReverseDependencies(packageID(id), ids)
   509  
   510  	// Make sure to delete the original package ID from the map.
   511  	delete(ids, packageID(id))
   512  
   513  	var pkgs []source.Package
   514  	for id := range ids {
   515  		pkg, err := s.checkedPackage(ctx, id, s.workspaceParseMode(id))
   516  		if err != nil {
   517  			return nil, err
   518  		}
   519  		pkgs = append(pkgs, pkg)
   520  	}
   521  	return pkgs, nil
   522  }
   523  
   524  func (s *snapshot) checkedPackage(ctx context.Context, id packageID, mode source.ParseMode) (*pkg, error) {
   525  	ph, err := s.buildPackageHandle(ctx, id, mode)
   526  	if err != nil {
   527  		return nil, err
   528  	}
   529  	return ph.check(ctx, s)
   530  }
   531  
   532  // transitiveReverseDependencies populates the uris map with file URIs
   533  // belonging to the provided package and its transitive reverse dependencies.
   534  func (s *snapshot) transitiveReverseDependencies(id packageID, ids map[packageID]struct{}) {
   535  	if _, ok := ids[id]; ok {
   536  		return
   537  	}
   538  	if s.getMetadata(id) == nil {
   539  		return
   540  	}
   541  	ids[id] = struct{}{}
   542  	importedBy := s.getImportedBy(id)
   543  	for _, parentID := range importedBy {
   544  		s.transitiveReverseDependencies(parentID, ids)
   545  	}
   546  }
   547  
   548  func (s *snapshot) getGoFile(key parseKey) *parseGoHandle {
   549  	s.mu.Lock()
   550  	defer s.mu.Unlock()
   551  	return s.goFiles[key]
   552  }
   553  
   554  func (s *snapshot) addGoFile(key parseKey, pgh *parseGoHandle) *parseGoHandle {
   555  	s.mu.Lock()
   556  	defer s.mu.Unlock()
   557  	if existing, ok := s.goFiles[key]; ok {
   558  		return existing
   559  	}
   560  	s.goFiles[key] = pgh
   561  	return pgh
   562  }
   563  
   564  func (s *snapshot) getParseModHandle(uri span.URI) *parseModHandle {
   565  	s.mu.Lock()
   566  	defer s.mu.Unlock()
   567  	return s.parseModHandles[uri]
   568  }
   569  
   570  func (s *snapshot) getModWhyHandle(uri span.URI) *modWhyHandle {
   571  	s.mu.Lock()
   572  	defer s.mu.Unlock()
   573  	return s.modWhyHandles[uri]
   574  }
   575  
   576  func (s *snapshot) getModUpgradeHandle(uri span.URI) *modUpgradeHandle {
   577  	s.mu.Lock()
   578  	defer s.mu.Unlock()
   579  	return s.modUpgradeHandles[uri]
   580  }
   581  
   582  func (s *snapshot) getModTidyHandle(uri span.URI) *modTidyHandle {
   583  	s.mu.Lock()
   584  	defer s.mu.Unlock()
   585  	return s.modTidyHandles[uri]
   586  }
   587  
   588  func (s *snapshot) getImportedBy(id packageID) []packageID {
   589  	s.mu.Lock()
   590  	defer s.mu.Unlock()
   591  	return s.getImportedByLocked(id)
   592  }
   593  
   594  func (s *snapshot) getImportedByLocked(id packageID) []packageID {
   595  	// If we haven't rebuilt the import graph since creating the snapshot.
   596  	if len(s.importedBy) == 0 {
   597  		s.rebuildImportGraph()
   598  	}
   599  	return s.importedBy[id]
   600  }
   601  
   602  func (s *snapshot) clearAndRebuildImportGraph() {
   603  	s.mu.Lock()
   604  	defer s.mu.Unlock()
   605  
   606  	// Completely invalidate the original map.
   607  	s.importedBy = make(map[packageID][]packageID)
   608  	s.rebuildImportGraph()
   609  }
   610  
   611  func (s *snapshot) rebuildImportGraph() {
   612  	for id, m := range s.metadata {
   613  		for _, importID := range m.deps {
   614  			s.importedBy[importID] = append(s.importedBy[importID], id)
   615  		}
   616  	}
   617  }
   618  
   619  func (s *snapshot) addPackageHandle(ph *packageHandle) *packageHandle {
   620  	s.mu.Lock()
   621  	defer s.mu.Unlock()
   622  
   623  	// If the package handle has already been cached,
   624  	// return the cached handle instead of overriding it.
   625  	if ph, ok := s.packages[ph.packageKey()]; ok {
   626  		return ph
   627  	}
   628  	s.packages[ph.packageKey()] = ph
   629  	return ph
   630  }
   631  
   632  func (s *snapshot) workspacePackageIDs() (ids []packageID) {
   633  	s.mu.Lock()
   634  	defer s.mu.Unlock()
   635  
   636  	for id := range s.workspacePackages {
   637  		ids = append(ids, id)
   638  	}
   639  	return ids
   640  }
   641  
   642  func (s *snapshot) fileWatchingGlobPatterns(ctx context.Context) map[string]struct{} {
   643  	// Work-around microsoft/vscode#100870 by making sure that we are,
   644  	// at least, watching the user's entire workspace. This will still be
   645  	// applied to every folder in the workspace.
   646  	patterns := map[string]struct{}{
   647  		"**/*.{go,mod,sum}": {},
   648  	}
   649  	dirs := s.workspace.dirs(ctx, s)
   650  	for _, dir := range dirs {
   651  		dirName := dir.Filename()
   652  
   653  		// If the directory is within the view's folder, we're already watching
   654  		// it with the pattern above.
   655  		if source.InDir(s.view.folder.Filename(), dirName) {
   656  			continue
   657  		}
   658  		// TODO(rstambler): If microsoft/vscode#3025 is resolved before
   659  		// microsoft/vscode#101042, we will need a work-around for Windows
   660  		// drive letter casing.
   661  		patterns[fmt.Sprintf("%s/**/*.{go,mod,sum}", dirName)] = struct{}{}
   662  	}
   663  
   664  	// Some clients do not send notifications for changes to directories that
   665  	// contain Go code (golang/go#42348). To handle this, explicitly watch all
   666  	// of the directories in the workspace. We find them by adding the
   667  	// directories of every file in the snapshot's workspace directories.
   668  	var dirNames []string
   669  	for uri := range s.allKnownSubdirs(ctx) {
   670  		dirNames = append(dirNames, uri.Filename())
   671  	}
   672  	sort.Strings(dirNames)
   673  	if len(dirNames) > 0 {
   674  		patterns[fmt.Sprintf("{%s}", strings.Join(dirNames, ","))] = struct{}{}
   675  	}
   676  	return patterns
   677  }
   678  
   679  // allKnownSubdirs returns all of the subdirectories within the snapshot's
   680  // workspace directories. None of the workspace directories are included.
   681  func (s *snapshot) allKnownSubdirs(ctx context.Context) map[span.URI]struct{} {
   682  	dirs := s.workspace.dirs(ctx, s)
   683  
   684  	s.mu.Lock()
   685  	defer s.mu.Unlock()
   686  	seen := make(map[span.URI]struct{})
   687  	for uri := range s.files {
   688  		dir := filepath.Dir(uri.Filename())
   689  		var matched span.URI
   690  		for _, wsDir := range dirs {
   691  			if source.InDir(wsDir.Filename(), dir) {
   692  				matched = wsDir
   693  				break
   694  			}
   695  		}
   696  		// Don't watch any directory outside of the workspace directories.
   697  		if matched == "" {
   698  			continue
   699  		}
   700  		for {
   701  			if dir == "" || dir == matched.Filename() {
   702  				break
   703  			}
   704  			uri := span.URIFromPath(dir)
   705  			if _, ok := seen[uri]; ok {
   706  				break
   707  			}
   708  			seen[uri] = struct{}{}
   709  			dir = filepath.Dir(dir)
   710  		}
   711  	}
   712  	return seen
   713  }
   714  
   715  // knownFilesInDir returns the files known to the given snapshot that are in
   716  // the given directory. It does not respect symlinks.
   717  func (s *snapshot) knownFilesInDir(ctx context.Context, dir span.URI) []span.URI {
   718  	var files []span.URI
   719  	for uri := range s.files {
   720  		if source.InDir(dir.Filename(), uri.Filename()) {
   721  			files = append(files, uri)
   722  		}
   723  	}
   724  	return files
   725  }
   726  
   727  func (s *snapshot) WorkspacePackages(ctx context.Context) ([]source.Package, error) {
   728  	if err := s.awaitLoaded(ctx); err != nil {
   729  		return nil, err
   730  	}
   731  	var pkgs []source.Package
   732  	for _, pkgID := range s.workspacePackageIDs() {
   733  		pkg, err := s.checkedPackage(ctx, pkgID, s.workspaceParseMode(pkgID))
   734  		if err != nil {
   735  			return nil, err
   736  		}
   737  		pkgs = append(pkgs, pkg)
   738  	}
   739  	return pkgs, nil
   740  }
   741  
   742  func (s *snapshot) KnownPackages(ctx context.Context) ([]source.Package, error) {
   743  	if err := s.awaitLoaded(ctx); err != nil {
   744  		return nil, err
   745  	}
   746  
   747  	// The WorkspaceSymbols implementation relies on this function returning
   748  	// workspace packages first.
   749  	ids := s.workspacePackageIDs()
   750  	s.mu.Lock()
   751  	for id := range s.metadata {
   752  		if _, ok := s.workspacePackages[id]; ok {
   753  			continue
   754  		}
   755  		ids = append(ids, id)
   756  	}
   757  	s.mu.Unlock()
   758  
   759  	var pkgs []source.Package
   760  	for _, id := range ids {
   761  		pkg, err := s.checkedPackage(ctx, id, s.workspaceParseMode(id))
   762  		if err != nil {
   763  			return nil, err
   764  		}
   765  		pkgs = append(pkgs, pkg)
   766  	}
   767  	return pkgs, nil
   768  }
   769  
   770  func (s *snapshot) CachedImportPaths(ctx context.Context) (map[string]source.Package, error) {
   771  	// Don't reload workspace package metadata.
   772  	// This function is meant to only return currently cached information.
   773  	s.AwaitInitialized(ctx)
   774  
   775  	s.mu.Lock()
   776  	defer s.mu.Unlock()
   777  
   778  	results := map[string]source.Package{}
   779  	for _, ph := range s.packages {
   780  		cachedPkg, err := ph.cached(s.generation)
   781  		if err != nil {
   782  			continue
   783  		}
   784  		for importPath, newPkg := range cachedPkg.imports {
   785  			if oldPkg, ok := results[string(importPath)]; ok {
   786  				// Using the same trick as NarrowestPackage, prefer non-variants.
   787  				if len(newPkg.compiledGoFiles) < len(oldPkg.(*pkg).compiledGoFiles) {
   788  					results[string(importPath)] = newPkg
   789  				}
   790  			} else {
   791  				results[string(importPath)] = newPkg
   792  			}
   793  		}
   794  	}
   795  	return results, nil
   796  }
   797  
   798  func (s *snapshot) GoModForFile(ctx context.Context, uri span.URI) span.URI {
   799  	var match span.URI
   800  	for modURI := range s.workspace.getActiveModFiles() {
   801  		if !source.InDir(dirURI(modURI).Filename(), uri.Filename()) {
   802  			continue
   803  		}
   804  		if len(modURI) > len(match) {
   805  			match = modURI
   806  		}
   807  	}
   808  	return match
   809  }
   810  
   811  func (s *snapshot) getPackage(id packageID, mode source.ParseMode) *packageHandle {
   812  	s.mu.Lock()
   813  	defer s.mu.Unlock()
   814  
   815  	key := packageKey{
   816  		id:   id,
   817  		mode: mode,
   818  	}
   819  	return s.packages[key]
   820  }
   821  
   822  func (s *snapshot) getActionHandle(id packageID, m source.ParseMode, a *analysis.Analyzer) *actionHandle {
   823  	s.mu.Lock()
   824  	defer s.mu.Unlock()
   825  
   826  	key := actionKey{
   827  		pkg: packageKey{
   828  			id:   id,
   829  			mode: m,
   830  		},
   831  		analyzer: a,
   832  	}
   833  	return s.actions[key]
   834  }
   835  
   836  func (s *snapshot) addActionHandle(ah *actionHandle) *actionHandle {
   837  	s.mu.Lock()
   838  	defer s.mu.Unlock()
   839  
   840  	key := actionKey{
   841  		analyzer: ah.analyzer,
   842  		pkg: packageKey{
   843  			id:   ah.pkg.m.id,
   844  			mode: ah.pkg.mode,
   845  		},
   846  	}
   847  	if ah, ok := s.actions[key]; ok {
   848  		return ah
   849  	}
   850  	s.actions[key] = ah
   851  	return ah
   852  }
   853  
   854  func (s *snapshot) getIDsForURI(uri span.URI) []packageID {
   855  	s.mu.Lock()
   856  	defer s.mu.Unlock()
   857  
   858  	return s.ids[uri]
   859  }
   860  
   861  func (s *snapshot) getMetadataForURILocked(uri span.URI) (metadata []*metadata) {
   862  	// TODO(matloob): uri can be a file or directory. Should we update the mappings
   863  	// to map directories to their contained packages?
   864  
   865  	for _, id := range s.ids[uri] {
   866  		if m, ok := s.metadata[id]; ok {
   867  			metadata = append(metadata, m)
   868  		}
   869  	}
   870  	return metadata
   871  }
   872  
   873  func (s *snapshot) getMetadata(id packageID) *metadata {
   874  	s.mu.Lock()
   875  	defer s.mu.Unlock()
   876  
   877  	return s.metadata[id]
   878  }
   879  
   880  func (s *snapshot) addID(uri span.URI, id packageID) {
   881  	s.mu.Lock()
   882  	defer s.mu.Unlock()
   883  
   884  	for i, existingID := range s.ids[uri] {
   885  		// TODO: We should make sure not to set duplicate IDs,
   886  		// and instead panic here. This can be done by making sure not to
   887  		// reset metadata information for packages we've already seen.
   888  		if existingID == id {
   889  			return
   890  		}
   891  		// If we are setting a real ID, when the package had only previously
   892  		// had a command-line-arguments ID, we should just replace it.
   893  		if existingID == "command-line-arguments" {
   894  			s.ids[uri][i] = id
   895  			// Delete command-line-arguments if it was a workspace package.
   896  			delete(s.workspacePackages, existingID)
   897  			return
   898  		}
   899  	}
   900  	s.ids[uri] = append(s.ids[uri], id)
   901  }
   902  
   903  func (s *snapshot) isWorkspacePackage(id packageID) (packagePath, bool) {
   904  	s.mu.Lock()
   905  	defer s.mu.Unlock()
   906  
   907  	scope, ok := s.workspacePackages[id]
   908  	return scope, ok
   909  }
   910  
   911  func (s *snapshot) FindFile(uri span.URI) source.VersionedFileHandle {
   912  	f, err := s.view.getFile(uri)
   913  	if err != nil {
   914  		return nil
   915  	}
   916  
   917  	s.mu.Lock()
   918  	defer s.mu.Unlock()
   919  
   920  	return s.files[f.URI()]
   921  }
   922  
   923  // GetVersionedFile returns a File for the given URI. If the file is unknown it
   924  // is added to the managed set.
   925  //
   926  // GetVersionedFile succeeds even if the file does not exist. A non-nil error return
   927  // indicates some type of internal error, for example if ctx is cancelled.
   928  func (s *snapshot) GetVersionedFile(ctx context.Context, uri span.URI) (source.VersionedFileHandle, error) {
   929  	f, err := s.view.getFile(uri)
   930  	if err != nil {
   931  		return nil, err
   932  	}
   933  
   934  	s.mu.Lock()
   935  	defer s.mu.Unlock()
   936  	return s.getFileLocked(ctx, f)
   937  }
   938  
   939  // GetFile implements the fileSource interface by wrapping GetVersionedFile.
   940  func (s *snapshot) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
   941  	return s.GetVersionedFile(ctx, uri)
   942  }
   943  
   944  func (s *snapshot) getFileLocked(ctx context.Context, f *fileBase) (source.VersionedFileHandle, error) {
   945  	if fh, ok := s.files[f.URI()]; ok {
   946  		return fh, nil
   947  	}
   948  
   949  	fh, err := s.view.session.cache.getFile(ctx, f.URI())
   950  	if err != nil {
   951  		return nil, err
   952  	}
   953  	closed := &closedFile{fh}
   954  	s.files[f.URI()] = closed
   955  	return closed, nil
   956  }
   957  
   958  func (s *snapshot) IsOpen(uri span.URI) bool {
   959  	s.mu.Lock()
   960  	defer s.mu.Unlock()
   961  	return s.isOpenLocked(uri)
   962  
   963  }
   964  
   965  func (s *snapshot) isOpenLocked(uri span.URI) bool {
   966  	_, open := s.files[uri].(*overlay)
   967  	return open
   968  }
   969  
   970  func (s *snapshot) awaitLoaded(ctx context.Context) error {
   971  	// Do not return results until the snapshot's view has been initialized.
   972  	s.AwaitInitialized(ctx)
   973  
   974  	if err := s.reloadWorkspace(ctx); err != nil {
   975  		return err
   976  	}
   977  	if err := s.reloadOrphanedFiles(ctx); err != nil {
   978  		return err
   979  	}
   980  	// If we still have absolutely no metadata, check if the view failed to
   981  	// initialize and return any errors.
   982  	// TODO(rstambler): Should we clear the error after we return it?
   983  	s.mu.Lock()
   984  	defer s.mu.Unlock()
   985  	if len(s.metadata) == 0 {
   986  		return s.initializedErr
   987  	}
   988  	return nil
   989  }
   990  
   991  func (s *snapshot) AwaitInitialized(ctx context.Context) {
   992  	select {
   993  	case <-ctx.Done():
   994  		return
   995  	case <-s.view.initialWorkspaceLoad:
   996  	}
   997  	// We typically prefer to run something as intensive as the IWL without
   998  	// blocking. I'm not sure if there is a way to do that here.
   999  	s.initialize(ctx, false)
  1000  }
  1001  
  1002  // reloadWorkspace reloads the metadata for all invalidated workspace packages.
  1003  func (s *snapshot) reloadWorkspace(ctx context.Context) error {
  1004  	// See which of the workspace packages are missing metadata.
  1005  	s.mu.Lock()
  1006  	missingMetadata := len(s.workspacePackages) == 0 || len(s.metadata) == 0
  1007  	pkgPathSet := map[packagePath]struct{}{}
  1008  	for id, pkgPath := range s.workspacePackages {
  1009  		if s.metadata[id] != nil {
  1010  			continue
  1011  		}
  1012  		missingMetadata = true
  1013  
  1014  		// Don't try to reload "command-line-arguments" directly.
  1015  		if pkgPath == "command-line-arguments" {
  1016  			continue
  1017  		}
  1018  		pkgPathSet[pkgPath] = struct{}{}
  1019  	}
  1020  	s.mu.Unlock()
  1021  
  1022  	// If the view's build configuration is invalid, we cannot reload by
  1023  	// package path. Just reload the directory instead.
  1024  	if missingMetadata && !s.ValidBuildConfiguration() {
  1025  		return s.load(ctx, false, viewLoadScope("LOAD_INVALID_VIEW"))
  1026  	}
  1027  
  1028  	if len(pkgPathSet) == 0 {
  1029  		return nil
  1030  	}
  1031  
  1032  	var pkgPaths []interface{}
  1033  	for pkgPath := range pkgPathSet {
  1034  		pkgPaths = append(pkgPaths, pkgPath)
  1035  	}
  1036  	return s.load(ctx, false, pkgPaths...)
  1037  }
  1038  
  1039  func (s *snapshot) reloadOrphanedFiles(ctx context.Context) error {
  1040  	// When we load ./... or a package path directly, we may not get packages
  1041  	// that exist only in overlays. As a workaround, we search all of the files
  1042  	// available in the snapshot and reload their metadata individually using a
  1043  	// file= query if the metadata is unavailable.
  1044  	scopes := s.orphanedFileScopes()
  1045  	if len(scopes) == 0 {
  1046  		return nil
  1047  	}
  1048  
  1049  	err := s.load(ctx, false, scopes...)
  1050  
  1051  	// If we failed to load some files, i.e. they have no metadata,
  1052  	// mark the failures so we don't bother retrying until the file's
  1053  	// content changes.
  1054  	//
  1055  	// TODO(rstambler): This may be an overestimate if the load stopped
  1056  	// early for an unrelated errors. Add a fallback?
  1057  	//
  1058  	// Check for context cancellation so that we don't incorrectly mark files
  1059  	// as unloadable, but don't return before setting all workspace packages.
  1060  	if ctx.Err() == nil && err != nil {
  1061  		event.Error(ctx, "reloadOrphanedFiles: failed to load", err, tag.Query.Of(scopes))
  1062  		s.mu.Lock()
  1063  		for _, scope := range scopes {
  1064  			uri := span.URI(scope.(fileURI))
  1065  			if s.getMetadataForURILocked(uri) == nil {
  1066  				s.unloadableFiles[uri] = struct{}{}
  1067  			}
  1068  		}
  1069  		s.mu.Unlock()
  1070  	}
  1071  	return nil
  1072  }
  1073  
  1074  func (s *snapshot) orphanedFileScopes() []interface{} {
  1075  	s.mu.Lock()
  1076  	defer s.mu.Unlock()
  1077  
  1078  	scopeSet := make(map[span.URI]struct{})
  1079  	for uri, fh := range s.files {
  1080  		// Don't try to reload metadata for go.mod files.
  1081  		if fh.Kind() != source.Go {
  1082  			continue
  1083  		}
  1084  		// If the URI doesn't belong to this view, then it's not in a workspace
  1085  		// package and should not be reloaded directly.
  1086  		if !contains(s.view.session.viewsOf(uri), s.view) {
  1087  			continue
  1088  		}
  1089  		// If the file is not open and is in a vendor directory, don't treat it
  1090  		// like a workspace package.
  1091  		if _, ok := fh.(*overlay); !ok && inVendor(uri) {
  1092  			continue
  1093  		}
  1094  		// Don't reload metadata for files we've already deemed unloadable.
  1095  		if _, ok := s.unloadableFiles[uri]; ok {
  1096  			continue
  1097  		}
  1098  		if s.getMetadataForURILocked(uri) == nil {
  1099  			scopeSet[uri] = struct{}{}
  1100  		}
  1101  	}
  1102  	var scopes []interface{}
  1103  	for uri := range scopeSet {
  1104  		scopes = append(scopes, fileURI(uri))
  1105  	}
  1106  	return scopes
  1107  }
  1108  
  1109  func contains(views []*View, view *View) bool {
  1110  	for _, v := range views {
  1111  		if v == view {
  1112  			return true
  1113  		}
  1114  	}
  1115  	return false
  1116  }
  1117  
  1118  func inVendor(uri span.URI) bool {
  1119  	toSlash := filepath.ToSlash(uri.Filename())
  1120  	if !strings.Contains(toSlash, "/vendor/") {
  1121  		return false
  1122  	}
  1123  	// Only packages in _subdirectories_ of /vendor/ are considered vendored
  1124  	// (/vendor/a/foo.go is vendored, /vendor/foo.go is not).
  1125  	split := strings.Split(toSlash, "/vendor/")
  1126  	if len(split) < 2 {
  1127  		return false
  1128  	}
  1129  	return strings.Contains(split[1], "/")
  1130  }
  1131  
  1132  func generationName(v *View, snapshotID uint64) string {
  1133  	return fmt.Sprintf("v%v/%v", v.id, snapshotID)
  1134  }
  1135  
  1136  func (s *snapshot) clone(ctx, bgCtx context.Context, changes map[span.URI]*fileChange, forceReloadMetadata bool) (*snapshot, bool) {
  1137  	// Track some important types of changes.
  1138  	var (
  1139  		vendorChanged  bool
  1140  		modulesChanged bool
  1141  	)
  1142  	newWorkspace, workspaceChanged := s.workspace.invalidate(ctx, changes)
  1143  
  1144  	s.mu.Lock()
  1145  	defer s.mu.Unlock()
  1146  
  1147  	newGen := s.view.session.cache.store.Generation(generationName(s.view, s.id+1))
  1148  	bgCtx, cancel := context.WithCancel(bgCtx)
  1149  	result := &snapshot{
  1150  		id:                s.id + 1,
  1151  		generation:        newGen,
  1152  		view:              s.view,
  1153  		backgroundCtx:     bgCtx,
  1154  		cancel:            cancel,
  1155  		builtin:           s.builtin,
  1156  		initializeOnce:    s.initializeOnce,
  1157  		initializedErr:    s.initializedErr,
  1158  		ids:               make(map[span.URI][]packageID),
  1159  		importedBy:        make(map[packageID][]packageID),
  1160  		metadata:          make(map[packageID]*metadata),
  1161  		packages:          make(map[packageKey]*packageHandle),
  1162  		actions:           make(map[actionKey]*actionHandle),
  1163  		files:             make(map[span.URI]source.VersionedFileHandle),
  1164  		goFiles:           make(map[parseKey]*parseGoHandle),
  1165  		workspacePackages: make(map[packageID]packagePath),
  1166  		unloadableFiles:   make(map[span.URI]struct{}),
  1167  		parseModHandles:   make(map[span.URI]*parseModHandle),
  1168  		modTidyHandles:    make(map[span.URI]*modTidyHandle),
  1169  		modUpgradeHandles: make(map[span.URI]*modUpgradeHandle),
  1170  		modWhyHandles:     make(map[span.URI]*modWhyHandle),
  1171  		workspace:         newWorkspace,
  1172  	}
  1173  
  1174  	if !workspaceChanged && s.workspaceDirHandle != nil {
  1175  		result.workspaceDirHandle = s.workspaceDirHandle
  1176  		newGen.Inherit(s.workspaceDirHandle)
  1177  	}
  1178  
  1179  	if s.builtin != nil {
  1180  		newGen.Inherit(s.builtin.handle)
  1181  	}
  1182  
  1183  	// Copy all of the FileHandles.
  1184  	for k, v := range s.files {
  1185  		result.files[k] = v
  1186  	}
  1187  
  1188  	// Copy the set of unloadable files.
  1189  	for k, v := range s.unloadableFiles {
  1190  		result.unloadableFiles[k] = v
  1191  	}
  1192  	// Copy all of the modHandles.
  1193  	for k, v := range s.parseModHandles {
  1194  		result.parseModHandles[k] = v
  1195  	}
  1196  
  1197  	for k, v := range s.goFiles {
  1198  		if _, ok := changes[k.file.URI]; ok {
  1199  			continue
  1200  		}
  1201  		newGen.Inherit(v.handle)
  1202  		newGen.Inherit(v.astCacheHandle)
  1203  		result.goFiles[k] = v
  1204  	}
  1205  
  1206  	// Copy all of the go.mod-related handles. They may be invalidated later,
  1207  	// so we inherit them at the end of the function.
  1208  	for k, v := range s.modTidyHandles {
  1209  		if _, ok := changes[k]; ok {
  1210  			continue
  1211  		}
  1212  		result.modTidyHandles[k] = v
  1213  	}
  1214  	for k, v := range s.modUpgradeHandles {
  1215  		if _, ok := changes[k]; ok {
  1216  			continue
  1217  		}
  1218  		result.modUpgradeHandles[k] = v
  1219  	}
  1220  	for k, v := range s.modWhyHandles {
  1221  		if _, ok := changes[k]; ok {
  1222  			continue
  1223  		}
  1224  		result.modWhyHandles[k] = v
  1225  	}
  1226  
  1227  	// directIDs keeps track of package IDs that have directly changed.
  1228  	// It maps id->invalidateMetadata.
  1229  	directIDs := map[packageID]bool{}
  1230  	// Invalidate all package metadata if the workspace module has changed.
  1231  	if workspaceChanged {
  1232  		for k := range s.metadata {
  1233  			directIDs[k] = true
  1234  		}
  1235  	}
  1236  
  1237  	changedPkgNames := map[packageID][]span.URI{}
  1238  	for uri, change := range changes {
  1239  		// Maybe reinitialize the view if we see a change in the vendor
  1240  		// directory.
  1241  		if inVendor(uri) {
  1242  			vendorChanged = true
  1243  		}
  1244  
  1245  		// The original FileHandle for this URI is cached on the snapshot.
  1246  		originalFH := s.files[uri]
  1247  
  1248  		// Check if the file's package name or imports have changed,
  1249  		// and if so, invalidate this file's packages' metadata.
  1250  		shouldInvalidateMetadata, pkgNameChanged := s.shouldInvalidateMetadata(ctx, result, originalFH, change.fileHandle)
  1251  		invalidateMetadata := forceReloadMetadata || shouldInvalidateMetadata
  1252  
  1253  		// Mark all of the package IDs containing the given file.
  1254  		// TODO: if the file has moved into a new package, we should invalidate that too.
  1255  		filePackageIDs := guessPackageIDsForURI(uri, s.ids)
  1256  		if pkgNameChanged {
  1257  			for _, id := range filePackageIDs {
  1258  				changedPkgNames[id] = append(changedPkgNames[id], uri)
  1259  			}
  1260  		}
  1261  		for _, id := range filePackageIDs {
  1262  			directIDs[id] = directIDs[id] || invalidateMetadata
  1263  		}
  1264  
  1265  		// Invalidate the previous modTidyHandle if any of the files have been
  1266  		// saved or if any of the metadata has been invalidated.
  1267  		if invalidateMetadata || fileWasSaved(originalFH, change.fileHandle) {
  1268  			// TODO(rstambler): Only delete mod handles for which the
  1269  			// withoutURI is relevant.
  1270  			for k := range s.modTidyHandles {
  1271  				delete(result.modTidyHandles, k)
  1272  			}
  1273  			for k := range s.modUpgradeHandles {
  1274  				delete(result.modUpgradeHandles, k)
  1275  			}
  1276  			for k := range s.modWhyHandles {
  1277  				delete(result.modWhyHandles, k)
  1278  			}
  1279  		}
  1280  		if isGoMod(uri) {
  1281  			// If the view's go.mod file's contents have changed, invalidate
  1282  			// the metadata for every known package in the snapshot.
  1283  			delete(result.parseModHandles, uri)
  1284  			if _, ok := result.workspace.getActiveModFiles()[uri]; ok {
  1285  				modulesChanged = true
  1286  			}
  1287  		}
  1288  		// Handle the invalidated file; it may have new contents or not exist.
  1289  		if !change.exists {
  1290  			delete(result.files, uri)
  1291  		} else {
  1292  			result.files[uri] = change.fileHandle
  1293  		}
  1294  		// Make sure to remove the changed file from the unloadable set.
  1295  		delete(result.unloadableFiles, uri)
  1296  	}
  1297  
  1298  	// Invalidate reverse dependencies too.
  1299  	// TODO(heschi): figure out the locking model and use transitiveReverseDeps?
  1300  	// transitiveIDs keeps track of transitive reverse dependencies.
  1301  	// If an ID is present in the map, invalidate its types.
  1302  	// If an ID's value is true, invalidate its metadata too.
  1303  	transitiveIDs := make(map[packageID]bool)
  1304  	var addRevDeps func(packageID, bool)
  1305  	addRevDeps = func(id packageID, invalidateMetadata bool) {
  1306  		current, seen := transitiveIDs[id]
  1307  		newInvalidateMetadata := current || invalidateMetadata
  1308  
  1309  		// If we've already seen this ID, and the value of invalidate
  1310  		// metadata has not changed, we can return early.
  1311  		if seen && current == newInvalidateMetadata {
  1312  			return
  1313  		}
  1314  		transitiveIDs[id] = newInvalidateMetadata
  1315  		for _, rid := range s.getImportedByLocked(id) {
  1316  			addRevDeps(rid, invalidateMetadata)
  1317  		}
  1318  	}
  1319  	for id, invalidateMetadata := range directIDs {
  1320  		addRevDeps(id, invalidateMetadata)
  1321  	}
  1322  
  1323  	// Copy the package type information.
  1324  	for k, v := range s.packages {
  1325  		if _, ok := transitiveIDs[k.id]; ok {
  1326  			continue
  1327  		}
  1328  		newGen.Inherit(v.handle)
  1329  		result.packages[k] = v
  1330  	}
  1331  	// Copy the package analysis information.
  1332  	for k, v := range s.actions {
  1333  		if _, ok := transitiveIDs[k.pkg.id]; ok {
  1334  			continue
  1335  		}
  1336  		newGen.Inherit(v.handle)
  1337  		result.actions[k] = v
  1338  	}
  1339  	// Copy the package metadata. We only need to invalidate packages directly
  1340  	// containing the affected file, and only if it changed in a relevant way.
  1341  	for k, v := range s.metadata {
  1342  		if invalidateMetadata, ok := transitiveIDs[k]; invalidateMetadata && ok {
  1343  			continue
  1344  		}
  1345  		result.metadata[k] = v
  1346  	}
  1347  	// Copy the URI to package ID mappings, skipping only those URIs whose
  1348  	// metadata will be reloaded in future calls to load.
  1349  copyIDs:
  1350  	for k, ids := range s.ids {
  1351  		for _, id := range ids {
  1352  			if invalidateMetadata, ok := transitiveIDs[id]; invalidateMetadata && ok {
  1353  				continue copyIDs
  1354  			}
  1355  		}
  1356  		result.ids[k] = ids
  1357  	}
  1358  	// Copy the set of initially loaded packages.
  1359  	for id, pkgPath := range s.workspacePackages {
  1360  		// Packages with the id "command-line-arguments" are generated by the
  1361  		// go command when the user is outside of GOPATH and outside of a
  1362  		// module. Do not cache them as workspace packages for longer than
  1363  		// necessary.
  1364  		if id == "command-line-arguments" {
  1365  			if invalidateMetadata, ok := transitiveIDs[id]; invalidateMetadata && ok {
  1366  				continue
  1367  			}
  1368  		}
  1369  
  1370  		// If all the files we know about in a package have been deleted,
  1371  		// the package is gone and we should no longer try to load it.
  1372  		if m := s.metadata[id]; m != nil {
  1373  			hasFiles := false
  1374  			for _, uri := range s.metadata[id].goFiles {
  1375  				if _, ok := result.files[uri]; ok {
  1376  					hasFiles = true
  1377  					break
  1378  				}
  1379  			}
  1380  			if !hasFiles {
  1381  				continue
  1382  			}
  1383  		}
  1384  
  1385  		// If the package name of a file in the package has changed, it's
  1386  		// possible that the package ID may no longer exist.
  1387  		if uris, ok := changedPkgNames[id]; ok && s.shouldDeleteWorkspacePackageID(id, uris) {
  1388  			continue
  1389  		}
  1390  
  1391  		result.workspacePackages[id] = pkgPath
  1392  	}
  1393  
  1394  	// Inherit all of the go.mod-related handles.
  1395  	for _, v := range result.modTidyHandles {
  1396  		newGen.Inherit(v.handle)
  1397  	}
  1398  	for _, v := range result.modUpgradeHandles {
  1399  		newGen.Inherit(v.handle)
  1400  	}
  1401  	for _, v := range result.modWhyHandles {
  1402  		newGen.Inherit(v.handle)
  1403  	}
  1404  	for _, v := range result.parseModHandles {
  1405  		newGen.Inherit(v.handle)
  1406  	}
  1407  	// Don't bother copying the importedBy graph,
  1408  	// as it changes each time we update metadata.
  1409  
  1410  	// If the snapshot's workspace mode has changed, the packages loaded using
  1411  	// the previous mode are no longer relevant, so clear them out.
  1412  	if s.workspaceMode() != result.workspaceMode() {
  1413  		result.workspacePackages = map[packageID]packagePath{}
  1414  	}
  1415  
  1416  	// The snapshot may need to be reinitialized.
  1417  	if modulesChanged || workspaceChanged || vendorChanged {
  1418  		if workspaceChanged || result.initializedErr != nil {
  1419  			result.initializeOnce = &sync.Once{}
  1420  		}
  1421  	}
  1422  	return result, workspaceChanged
  1423  }
  1424  
  1425  // shouldDeleteWorkspacePackageID reports whether the given package ID should
  1426  // be removed from the set of workspace packages. If one of the files in the
  1427  // package has changed package names, we check if it is the only file that
  1428  // *only* belongs to this package. For example, in the case of a test variant,
  1429  // confirm that it is the sole file constituting the test variant.
  1430  func (s *snapshot) shouldDeleteWorkspacePackageID(id packageID, changedPkgNames []span.URI) bool {
  1431  	m, ok := s.metadata[id]
  1432  	if !ok {
  1433  		return false
  1434  	}
  1435  	changedPkgName := func(uri span.URI) bool {
  1436  		for _, changed := range changedPkgNames {
  1437  			if uri == changed {
  1438  				return true
  1439  			}
  1440  		}
  1441  		return false
  1442  	}
  1443  	for _, uri := range m.compiledGoFiles {
  1444  		if changedPkgName(uri) {
  1445  			continue
  1446  		}
  1447  		// If there is at least one file remaining that belongs only to this
  1448  		// package, and its package name has not changed, we shouldn't delete
  1449  		// its package ID from the set of workspace packages.
  1450  		if ids := guessPackageIDsForURI(uri, s.ids); len(ids) == 1 && ids[0] == id {
  1451  			return false
  1452  		}
  1453  	}
  1454  	return true
  1455  }
  1456  
  1457  // guessPackageIDsForURI returns all packages related to uri. If we haven't
  1458  // seen this URI before, we guess based on files in the same directory. This
  1459  // is of course incorrect in build systems where packages are not organized by
  1460  // directory.
  1461  func guessPackageIDsForURI(uri span.URI, known map[span.URI][]packageID) []packageID {
  1462  	packages := known[uri]
  1463  	if len(packages) > 0 {
  1464  		// We've seen this file before.
  1465  		return packages
  1466  	}
  1467  	// This is a file we don't yet know about. Guess relevant packages by
  1468  	// considering files in the same directory.
  1469  
  1470  	// Cache of FileInfo to avoid unnecessary stats for multiple files in the
  1471  	// same directory.
  1472  	stats := make(map[string]struct {
  1473  		os.FileInfo
  1474  		error
  1475  	})
  1476  	getInfo := func(dir string) (os.FileInfo, error) {
  1477  		if res, ok := stats[dir]; ok {
  1478  			return res.FileInfo, res.error
  1479  		}
  1480  		fi, err := os.Stat(dir)
  1481  		stats[dir] = struct {
  1482  			os.FileInfo
  1483  			error
  1484  		}{fi, err}
  1485  		return fi, err
  1486  	}
  1487  	dir := filepath.Dir(uri.Filename())
  1488  	fi, err := getInfo(dir)
  1489  	if err != nil {
  1490  		return nil
  1491  	}
  1492  
  1493  	// Aggregate all possibly relevant package IDs.
  1494  	var found []packageID
  1495  	for knownURI, ids := range known {
  1496  		knownDir := filepath.Dir(knownURI.Filename())
  1497  		knownFI, err := getInfo(knownDir)
  1498  		if err != nil {
  1499  			continue
  1500  		}
  1501  		if os.SameFile(fi, knownFI) {
  1502  			found = append(found, ids...)
  1503  		}
  1504  	}
  1505  	return found
  1506  }
  1507  
  1508  // fileWasSaved reports whether the FileHandle passed in has been saved. It
  1509  // accomplishes this by checking to see if the original and current FileHandles
  1510  // are both overlays, and if the current FileHandle is saved while the original
  1511  // FileHandle was not saved.
  1512  func fileWasSaved(originalFH, currentFH source.FileHandle) bool {
  1513  	c, ok := currentFH.(*overlay)
  1514  	if !ok || c == nil {
  1515  		return true
  1516  	}
  1517  	o, ok := originalFH.(*overlay)
  1518  	if !ok || o == nil {
  1519  		return c.saved
  1520  	}
  1521  	return !o.saved && c.saved
  1522  }
  1523  
  1524  // shouldInvalidateMetadata reparses a file's package and import declarations to
  1525  // determine if the file requires a metadata reload.
  1526  func (s *snapshot) shouldInvalidateMetadata(ctx context.Context, newSnapshot *snapshot, originalFH, currentFH source.FileHandle) (invalidate, pkgNameChanged bool) {
  1527  	if originalFH == nil {
  1528  		return true, false
  1529  	}
  1530  	// If the file hasn't changed, there's no need to reload.
  1531  	if originalFH.FileIdentity() == currentFH.FileIdentity() {
  1532  		return false, false
  1533  	}
  1534  	// If a go.mod in the workspace has been changed, invalidate metadata.
  1535  	if kind := originalFH.Kind(); kind == source.Mod {
  1536  		if !source.InDir(filepath.Dir(s.view.rootURI.Filename()), originalFH.URI().Filename()) {
  1537  			return false, false
  1538  		}
  1539  		return currentFH.Saved(), false
  1540  	}
  1541  	// Get the original and current parsed files in order to check package name
  1542  	// and imports. Use the new snapshot to parse to avoid modifying the
  1543  	// current snapshot.
  1544  	original, originalErr := newSnapshot.ParseGo(ctx, originalFH, source.ParseHeader)
  1545  	current, currentErr := newSnapshot.ParseGo(ctx, currentFH, source.ParseHeader)
  1546  	if originalErr != nil || currentErr != nil {
  1547  		return (originalErr == nil) != (currentErr == nil), false
  1548  	}
  1549  	// Check if the package's metadata has changed. The cases handled are:
  1550  	//    1. A package's name has changed
  1551  	//    2. A file's imports have changed
  1552  	if original.File.Name.Name != current.File.Name.Name {
  1553  		return true, true
  1554  	}
  1555  	importSet := make(map[string]struct{})
  1556  	for _, importSpec := range original.File.Imports {
  1557  		importSet[importSpec.Path.Value] = struct{}{}
  1558  	}
  1559  	// If any of the current imports were not in the original imports.
  1560  	for _, importSpec := range current.File.Imports {
  1561  		if _, ok := importSet[importSpec.Path.Value]; ok {
  1562  			continue
  1563  		}
  1564  		// If the import path is obviously not valid, we can skip reloading
  1565  		// metadata. For now, valid means properly quoted and without a
  1566  		// terminal slash.
  1567  		path, err := strconv.Unquote(importSpec.Path.Value)
  1568  		if err != nil {
  1569  			continue
  1570  		}
  1571  		if path == "" {
  1572  			continue
  1573  		}
  1574  		if path[len(path)-1] == '/' {
  1575  			continue
  1576  		}
  1577  		return true, false
  1578  	}
  1579  	return false, false
  1580  }
  1581  
  1582  func (s *snapshot) BuiltinPackage(ctx context.Context) (*source.BuiltinPackage, error) {
  1583  	s.AwaitInitialized(ctx)
  1584  
  1585  	if s.builtin == nil {
  1586  		return nil, errors.Errorf("no builtin package for view %s", s.view.name)
  1587  	}
  1588  	d, err := s.builtin.handle.Get(ctx, s.generation, s)
  1589  	if err != nil {
  1590  		return nil, err
  1591  	}
  1592  	data := d.(*builtinPackageData)
  1593  	return data.parsed, data.err
  1594  }
  1595  
  1596  func (s *snapshot) buildBuiltinPackage(ctx context.Context, goFiles []string) error {
  1597  	if len(goFiles) != 1 {
  1598  		return errors.Errorf("only expected 1 file, got %v", len(goFiles))
  1599  	}
  1600  	uri := span.URIFromPath(goFiles[0])
  1601  
  1602  	// Get the FileHandle through the cache to avoid adding it to the snapshot
  1603  	// and to get the file content from disk.
  1604  	fh, err := s.view.session.cache.getFile(ctx, uri)
  1605  	if err != nil {
  1606  		return err
  1607  	}
  1608  	h := s.generation.Bind(fh.FileIdentity(), func(ctx context.Context, arg memoize.Arg) interface{} {
  1609  		snapshot := arg.(*snapshot)
  1610  
  1611  		pgh := snapshot.parseGoHandle(ctx, fh, source.ParseFull)
  1612  		pgf, _, err := snapshot.parseGo(ctx, pgh)
  1613  		if err != nil {
  1614  			return &builtinPackageData{err: err}
  1615  		}
  1616  		pkg, err := ast.NewPackage(snapshot.view.session.cache.fset, map[string]*ast.File{
  1617  			pgf.URI.Filename(): pgf.File,
  1618  		}, nil, nil)
  1619  		if err != nil {
  1620  			return &builtinPackageData{err: err}
  1621  		}
  1622  		return &builtinPackageData{
  1623  			parsed: &source.BuiltinPackage{
  1624  				ParsedFile: pgf,
  1625  				Package:    pkg,
  1626  			},
  1627  		}
  1628  	}, nil)
  1629  	s.builtin = &builtinPackageHandle{handle: h}
  1630  	return nil
  1631  }
  1632  
  1633  // BuildGoplsMod generates a go.mod file for all modules in the workspace. It
  1634  // bypasses any existing gopls.mod.
  1635  func BuildGoplsMod(ctx context.Context, root span.URI, fs source.FileSource) (*modfile.File, error) {
  1636  	allModules, err := findModules(ctx, root, 0)
  1637  	if err != nil {
  1638  		return nil, err
  1639  	}
  1640  	return buildWorkspaceModFile(ctx, allModules, fs)
  1641  }
  1642  
  1643  // TODO(rfindley): move this to workspacemodule.go
  1644  func buildWorkspaceModFile(ctx context.Context, modFiles map[span.URI]struct{}, fs source.FileSource) (*modfile.File, error) {
  1645  	file := &modfile.File{}
  1646  	file.AddModuleStmt("gopls-workspace")
  1647  
  1648  	paths := make(map[string]span.URI)
  1649  	for modURI := range modFiles {
  1650  		fh, err := fs.GetFile(ctx, modURI)
  1651  		if err != nil {
  1652  			return nil, err
  1653  		}
  1654  		content, err := fh.Read()
  1655  		if err != nil {
  1656  			return nil, err
  1657  		}
  1658  		parsed, err := modfile.Parse(fh.URI().Filename(), content, nil)
  1659  		if err != nil {
  1660  			return nil, err
  1661  		}
  1662  		if file == nil || parsed.Module == nil {
  1663  			return nil, fmt.Errorf("no module declaration for %s", modURI)
  1664  		}
  1665  		path := parsed.Module.Mod.Path
  1666  		paths[path] = modURI
  1667  		// If the module's path includes a major version, we expect it to have
  1668  		// a matching major version.
  1669  		_, majorVersion, _ := module.SplitPathVersion(path)
  1670  		if majorVersion == "" {
  1671  			majorVersion = "/v0"
  1672  		}
  1673  		majorVersion = strings.TrimLeft(majorVersion, "/.") // handle gopkg.in versions
  1674  		file.AddNewRequire(path, source.WorkspaceModuleVersion(majorVersion), false)
  1675  		if err := file.AddReplace(path, "", dirURI(modURI).Filename(), ""); err != nil {
  1676  			return nil, err
  1677  		}
  1678  	}
  1679  	// Go back through all of the modules to handle any of their replace
  1680  	// statements.
  1681  	for modURI := range modFiles {
  1682  		fh, err := fs.GetFile(ctx, modURI)
  1683  		if err != nil {
  1684  			return nil, err
  1685  		}
  1686  		content, err := fh.Read()
  1687  		if err != nil {
  1688  			return nil, err
  1689  		}
  1690  		parsed, err := modfile.Parse(fh.URI().Filename(), content, nil)
  1691  		if err != nil {
  1692  			return nil, err
  1693  		}
  1694  		// If any of the workspace modules have replace directives, they need
  1695  		// to be reflected in the workspace module.
  1696  		for _, rep := range parsed.Replace {
  1697  			// Don't replace any modules that are in our workspace--we should
  1698  			// always use the version in the workspace.
  1699  			if _, ok := paths[rep.Old.Path]; ok {
  1700  				continue
  1701  			}
  1702  			newPath := rep.New.Path
  1703  			newVersion := rep.New.Version
  1704  			// If a replace points to a module in the workspace, make sure we
  1705  			// direct it to version of the module in the workspace.
  1706  			if m, ok := paths[rep.New.Path]; ok {
  1707  				newPath = dirURI(m).Filename()
  1708  				newVersion = ""
  1709  			} else if rep.New.Version == "" && !filepath.IsAbs(rep.New.Path) {
  1710  				// Make any relative paths absolute.
  1711  				newPath = filepath.Join(dirURI(modURI).Filename(), rep.New.Path)
  1712  			}
  1713  			if err := file.AddReplace(rep.Old.Path, rep.Old.Version, newPath, newVersion); err != nil {
  1714  				return nil, err
  1715  			}
  1716  		}
  1717  	}
  1718  	return file, nil
  1719  }