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

     1  // Copyright 2018 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 is the core of gopls: it is concerned with state
     6  // management, dependency analysis, and invalidation; and it holds the
     7  // machinery of type checking and modular static analysis. Its
     8  // principal types are [Session], [Folder], [View], [Snapshot],
     9  // [Cache], and [Package].
    10  package cache
    11  
    12  import (
    13  	"bytes"
    14  	"context"
    15  	"encoding/json"
    16  	"errors"
    17  	"fmt"
    18  	"os"
    19  	"os/exec"
    20  	"path"
    21  	"path/filepath"
    22  	"regexp"
    23  	"sort"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	"golang.org/x/tools/gopls/internal/cache/metadata"
    29  	"golang.org/x/tools/gopls/internal/file"
    30  	"golang.org/x/tools/gopls/internal/protocol"
    31  	"golang.org/x/tools/gopls/internal/settings"
    32  	"golang.org/x/tools/gopls/internal/util/maps"
    33  	"golang.org/x/tools/gopls/internal/util/pathutil"
    34  	"golang.org/x/tools/gopls/internal/util/slices"
    35  	"golang.org/x/tools/gopls/internal/vulncheck"
    36  	"golang.org/x/tools/internal/event"
    37  	"golang.org/x/tools/internal/gocommand"
    38  	"golang.org/x/tools/internal/imports"
    39  	"golang.org/x/tools/internal/xcontext"
    40  )
    41  
    42  // A Folder represents an LSP workspace folder, together with its per-folder
    43  // options and environment variables that affect build configuration.
    44  //
    45  // Folders (Name and Dir) are specified by the 'initialize' and subsequent
    46  // 'didChangeWorkspaceFolders' requests; their options come from
    47  // didChangeConfiguration.
    48  //
    49  // Folders must not be mutated, as they may be shared across multiple views.
    50  type Folder struct {
    51  	Dir     protocol.DocumentURI
    52  	Name    string // decorative name for UI; not necessarily unique
    53  	Options *settings.Options
    54  	Env     GoEnv
    55  }
    56  
    57  // GoEnv holds the environment variables and data from the Go command that is
    58  // required for operating on a workspace folder.
    59  type GoEnv struct {
    60  	// Go environment variables. These correspond directly with the Go env var of
    61  	// the same name.
    62  	GOOS        string
    63  	GOARCH      string
    64  	GOCACHE     string
    65  	GOMODCACHE  string
    66  	GOPATH      string
    67  	GOPRIVATE   string
    68  	GOFLAGS     string
    69  	GO111MODULE string
    70  
    71  	// Go version output.
    72  	GoVersion       int    // The X in Go 1.X
    73  	GoVersionOutput string // complete go version output
    74  
    75  	// OS environment variables (notably not go env).
    76  	GOWORK           string
    77  	GOPACKAGESDRIVER string
    78  }
    79  
    80  // View represents a single build for a workspace.
    81  //
    82  // A View is a logical build (the viewDefinition) along with a state of that
    83  // build (the Snapshot).
    84  type View struct {
    85  	id string // a unique string to identify this View in (e.g.) serialized Commands
    86  
    87  	*viewDefinition // build configuration
    88  
    89  	gocmdRunner *gocommand.Runner // limits go command concurrency
    90  
    91  	// baseCtx is the context handed to NewView. This is the parent of all
    92  	// background contexts created for this view.
    93  	baseCtx context.Context
    94  
    95  	importsState *importsState
    96  
    97  	// parseCache holds an LRU cache of recently parsed files.
    98  	parseCache *parseCache
    99  
   100  	// fs is the file source used to populate this view.
   101  	fs *overlayFS
   102  
   103  	// ignoreFilter is used for fast checking of ignored files.
   104  	ignoreFilter *ignoreFilter
   105  
   106  	// cancelInitialWorkspaceLoad can be used to terminate the view's first
   107  	// attempt at initialization.
   108  	cancelInitialWorkspaceLoad context.CancelFunc
   109  
   110  	snapshotMu sync.Mutex
   111  	snapshot   *Snapshot // latest snapshot; nil after shutdown has been called
   112  
   113  	// initialWorkspaceLoad is closed when the first workspace initialization has
   114  	// completed. If we failed to load, we only retry if the go.mod file changes,
   115  	// to avoid too many go/packages calls.
   116  	initialWorkspaceLoad chan struct{}
   117  
   118  	// initializationSema is used limit concurrent initialization of snapshots in
   119  	// the view. We use a channel instead of a mutex to avoid blocking when a
   120  	// context is canceled.
   121  	//
   122  	// This field (along with snapshot.initialized) guards against duplicate
   123  	// initialization of snapshots. Do not change it without adjusting snapshot
   124  	// accordingly.
   125  	initializationSema chan struct{}
   126  
   127  	// Document filters are constructed once, in View.filterFunc.
   128  	filterFuncOnce sync.Once
   129  	_filterFunc    func(protocol.DocumentURI) bool // only accessed by View.filterFunc
   130  }
   131  
   132  // definition implements the viewDefiner interface.
   133  func (v *View) definition() *viewDefinition { return v.viewDefinition }
   134  
   135  // A viewDefinition is a logical build, i.e. configuration (Folder) along with
   136  // a build directory and possibly an environment overlay (e.g. GOWORK=off or
   137  // GOOS, GOARCH=...) to affect the build.
   138  //
   139  // This type is immutable, and compared to see if the View needs to be
   140  // reconstructed.
   141  //
   142  // Note: whenever modifying this type, also modify the equivalence relation
   143  // implemented by viewDefinitionsEqual.
   144  //
   145  // TODO(golang/go#57979): viewDefinition should be sufficient for running
   146  // go/packages. Enforce this in the API.
   147  type viewDefinition struct {
   148  	folder *Folder // pointer comparison is OK, as any new Folder creates a new def
   149  
   150  	typ    ViewType
   151  	root   protocol.DocumentURI // root directory; where to run the Go command
   152  	gomod  protocol.DocumentURI // the nearest go.mod file, or ""
   153  	gowork protocol.DocumentURI // the nearest go.work file, or ""
   154  
   155  	// workspaceModFiles holds the set of mod files active in this snapshot.
   156  	//
   157  	// For a go.work workspace, this is the set of workspace modfiles. For a
   158  	// go.mod workspace, this contains the go.mod file defining the workspace
   159  	// root, as well as any locally replaced modules (if
   160  	// "includeReplaceInWorkspace" is set).
   161  	//
   162  	// TODO(rfindley): should we just run `go list -m` to compute this set?
   163  	workspaceModFiles    map[protocol.DocumentURI]struct{}
   164  	workspaceModFilesErr error // error encountered computing workspaceModFiles
   165  
   166  	// envOverlay holds additional environment to apply to this viewDefinition.
   167  	envOverlay map[string]string
   168  }
   169  
   170  // definition implements the viewDefiner interface.
   171  func (d *viewDefinition) definition() *viewDefinition { return d }
   172  
   173  // Type returns the ViewType type, which determines how go/packages are loaded
   174  // for this View.
   175  func (d *viewDefinition) Type() ViewType { return d.typ }
   176  
   177  // Root returns the view root, which determines where packages are loaded from.
   178  func (d *viewDefinition) Root() protocol.DocumentURI { return d.root }
   179  
   180  // GoMod returns the nearest go.mod file for this view's root, or "".
   181  func (d *viewDefinition) GoMod() protocol.DocumentURI { return d.gomod }
   182  
   183  // GoWork returns the nearest go.work file for this view's root, or "".
   184  func (d *viewDefinition) GoWork() protocol.DocumentURI { return d.gowork }
   185  
   186  // EnvOverlay returns a new sorted slice of environment variables (in the form
   187  // "k=v") for this view definition's env overlay.
   188  func (d *viewDefinition) EnvOverlay() []string {
   189  	var env []string
   190  	for k, v := range d.envOverlay {
   191  		env = append(env, fmt.Sprintf("%s=%s", k, v))
   192  	}
   193  	sort.Strings(env)
   194  	return env
   195  }
   196  
   197  // GOOS returns the effective GOOS value for this view definition, accounting
   198  // for its env overlay.
   199  func (d *viewDefinition) GOOS() string {
   200  	if goos, ok := d.envOverlay["GOOS"]; ok {
   201  		return goos
   202  	}
   203  	return d.folder.Env.GOOS
   204  }
   205  
   206  // GOOS returns the effective GOARCH value for this view definition, accounting
   207  // for its env overlay.
   208  func (d *viewDefinition) GOARCH() string {
   209  	if goarch, ok := d.envOverlay["GOARCH"]; ok {
   210  		return goarch
   211  	}
   212  	return d.folder.Env.GOARCH
   213  }
   214  
   215  // adjustedGO111MODULE is the value of GO111MODULE to use for loading packages.
   216  // It is adjusted to default to "auto" rather than "on", since if we are in
   217  // GOPATH and have no module, we may as well allow a GOPATH view to work.
   218  func (d viewDefinition) adjustedGO111MODULE() string {
   219  	if d.folder.Env.GO111MODULE != "" {
   220  		return d.folder.Env.GO111MODULE
   221  	}
   222  	return "auto"
   223  }
   224  
   225  // ModFiles are the go.mod files enclosed in the snapshot's view and known
   226  // to the snapshot.
   227  func (d viewDefinition) ModFiles() []protocol.DocumentURI {
   228  	var uris []protocol.DocumentURI
   229  	for modURI := range d.workspaceModFiles {
   230  		uris = append(uris, modURI)
   231  	}
   232  	return uris
   233  }
   234  
   235  // viewDefinitionsEqual reports whether x and y are equivalent.
   236  func viewDefinitionsEqual(x, y *viewDefinition) bool {
   237  	if (x.workspaceModFilesErr == nil) != (y.workspaceModFilesErr == nil) {
   238  		return false
   239  	}
   240  	if x.workspaceModFilesErr != nil {
   241  		if x.workspaceModFilesErr.Error() != y.workspaceModFilesErr.Error() {
   242  			return false
   243  		}
   244  	} else if !maps.SameKeys(x.workspaceModFiles, y.workspaceModFiles) {
   245  		return false
   246  	}
   247  	if len(x.envOverlay) != len(y.envOverlay) {
   248  		return false
   249  	}
   250  	for i, xv := range x.envOverlay {
   251  		if xv != y.envOverlay[i] {
   252  			return false
   253  		}
   254  	}
   255  	return x.folder == y.folder &&
   256  		x.typ == y.typ &&
   257  		x.root == y.root &&
   258  		x.gomod == y.gomod &&
   259  		x.gowork == y.gowork
   260  }
   261  
   262  // A ViewType describes how we load package information for a view.
   263  //
   264  // This is used for constructing the go/packages.Load query, and for
   265  // interpreting missing packages, imports, or errors.
   266  //
   267  // See the documentation for individual ViewType values for details.
   268  type ViewType int
   269  
   270  const (
   271  	// GoPackagesDriverView is a view with a non-empty GOPACKAGESDRIVER
   272  	// environment variable.
   273  	//
   274  	// Load: ./... from the workspace folder.
   275  	GoPackagesDriverView ViewType = iota
   276  
   277  	// GOPATHView is a view in GOPATH mode.
   278  	//
   279  	// I.e. in GOPATH, with GO111MODULE=off, or GO111MODULE=auto with no
   280  	// go.mod file.
   281  	//
   282  	// Load: ./... from the workspace folder.
   283  	GOPATHView
   284  
   285  	// GoModView is a view in module mode with a single Go module.
   286  	//
   287  	// Load: <modulePath>/... from the module root.
   288  	GoModView
   289  
   290  	// GoWorkView is a view in module mode with a go.work file.
   291  	//
   292  	// Load: <modulePath>/... from the workspace folder, for each module.
   293  	GoWorkView
   294  
   295  	// An AdHocView is a collection of files in a given directory, not in GOPATH
   296  	// or a module.
   297  	//
   298  	// Load: . from the workspace folder.
   299  	AdHocView
   300  )
   301  
   302  func (t ViewType) String() string {
   303  	switch t {
   304  	case GoPackagesDriverView:
   305  		return "GoPackagesDriverView"
   306  	case GOPATHView:
   307  		return "GOPATHView"
   308  	case GoModView:
   309  		return "GoModView"
   310  	case GoWorkView:
   311  		return "GoWorkView"
   312  	case AdHocView:
   313  		return "AdHocView"
   314  	default:
   315  		return "Unknown"
   316  	}
   317  }
   318  
   319  // moduleMode reports whether the view uses Go modules.
   320  func (w viewDefinition) moduleMode() bool {
   321  	switch w.typ {
   322  	case GoModView, GoWorkView:
   323  		return true
   324  	default:
   325  		return false
   326  	}
   327  }
   328  
   329  func (v *View) ID() string { return v.id }
   330  
   331  // tempModFile creates a temporary go.mod file based on the contents
   332  // of the given go.mod file. On success, it is the caller's
   333  // responsibility to call the cleanup function when the file is no
   334  // longer needed.
   335  func tempModFile(modURI protocol.DocumentURI, gomod, gosum []byte) (tmpURI protocol.DocumentURI, cleanup func(), err error) {
   336  	filenameHash := file.HashOf([]byte(modURI.Path()))
   337  	tmpMod, err := os.CreateTemp("", fmt.Sprintf("go.%s.*.mod", filenameHash))
   338  	if err != nil {
   339  		return "", nil, err
   340  	}
   341  	defer tmpMod.Close()
   342  
   343  	tmpURI = protocol.URIFromPath(tmpMod.Name())
   344  	tmpSumName := sumFilename(tmpURI)
   345  
   346  	if _, err := tmpMod.Write(gomod); err != nil {
   347  		return "", nil, err
   348  	}
   349  
   350  	// We use a distinct name here to avoid subtlety around the fact
   351  	// that both 'return' and 'defer' update the "cleanup" variable.
   352  	doCleanup := func() {
   353  		_ = os.Remove(tmpSumName)
   354  		_ = os.Remove(tmpURI.Path())
   355  	}
   356  
   357  	// Be careful to clean up if we return an error from this function.
   358  	defer func() {
   359  		if err != nil {
   360  			doCleanup()
   361  			cleanup = nil
   362  		}
   363  	}()
   364  
   365  	// Create an analogous go.sum, if one exists.
   366  	if gosum != nil {
   367  		if err := os.WriteFile(tmpSumName, gosum, 0655); err != nil {
   368  			return "", nil, err
   369  		}
   370  	}
   371  
   372  	return tmpURI, doCleanup, nil
   373  }
   374  
   375  // Folder returns the folder at the base of this view.
   376  func (v *View) Folder() *Folder {
   377  	return v.folder
   378  }
   379  
   380  // UpdateFolders updates the set of views for the new folders.
   381  //
   382  // Calling this causes each view to be reinitialized.
   383  func (s *Session) UpdateFolders(ctx context.Context, newFolders []*Folder) error {
   384  	s.viewMu.Lock()
   385  	defer s.viewMu.Unlock()
   386  
   387  	overlays := s.Overlays()
   388  	var openFiles []protocol.DocumentURI
   389  	for _, o := range overlays {
   390  		openFiles = append(openFiles, o.URI())
   391  	}
   392  
   393  	defs, err := selectViewDefs(ctx, s, newFolders, openFiles)
   394  	if err != nil {
   395  		return err
   396  	}
   397  	var newViews []*View
   398  	for _, def := range defs {
   399  		v, _, release := s.createView(ctx, def)
   400  		release()
   401  		newViews = append(newViews, v)
   402  	}
   403  	for _, v := range s.views {
   404  		v.shutdown()
   405  	}
   406  	s.views = newViews
   407  	return nil
   408  }
   409  
   410  // viewEnv returns a string describing the environment of a newly created view.
   411  //
   412  // It must not be called concurrently with any other view methods.
   413  // TODO(rfindley): rethink this function, or inline sole call.
   414  func viewEnv(v *View) string {
   415  	var buf bytes.Buffer
   416  	fmt.Fprintf(&buf, `go info for %v
   417  (view type %v)
   418  (root dir %s)
   419  (go version %s)
   420  (build flags: %v)
   421  (go env: %+v)
   422  (env overlay: %v)
   423  `,
   424  		v.folder.Dir.Path(),
   425  		v.typ,
   426  		v.root.Path(),
   427  		strings.TrimRight(v.folder.Env.GoVersionOutput, "\n"),
   428  		v.folder.Options.BuildFlags,
   429  		v.folder.Env,
   430  		v.envOverlay,
   431  	)
   432  
   433  	return buf.String()
   434  }
   435  
   436  // RunProcessEnvFunc runs fn with the process env for this snapshot's view.
   437  // Note: the process env contains cached module and filesystem state.
   438  func (s *Snapshot) RunProcessEnvFunc(ctx context.Context, fn func(context.Context, *imports.Options) error) error {
   439  	return s.view.importsState.runProcessEnvFunc(ctx, s, fn)
   440  }
   441  
   442  // separated out from its sole use in locateTemplateFiles for testability
   443  func fileHasExtension(path string, suffixes []string) bool {
   444  	ext := filepath.Ext(path)
   445  	if ext != "" && ext[0] == '.' {
   446  		ext = ext[1:]
   447  	}
   448  	for _, s := range suffixes {
   449  		if s != "" && ext == s {
   450  			return true
   451  		}
   452  	}
   453  	return false
   454  }
   455  
   456  // locateTemplateFiles ensures that the snapshot has mapped template files
   457  // within the workspace folder.
   458  func (s *Snapshot) locateTemplateFiles(ctx context.Context) {
   459  	suffixes := s.Options().TemplateExtensions
   460  	if len(suffixes) == 0 {
   461  		return
   462  	}
   463  
   464  	searched := 0
   465  	filterFunc := s.view.filterFunc()
   466  	err := filepath.WalkDir(s.view.folder.Dir.Path(), func(path string, entry os.DirEntry, err error) error {
   467  		if err != nil {
   468  			return err
   469  		}
   470  		if entry.IsDir() {
   471  			return nil
   472  		}
   473  		if fileLimit > 0 && searched > fileLimit {
   474  			return errExhausted
   475  		}
   476  		searched++
   477  		if !fileHasExtension(path, suffixes) {
   478  			return nil
   479  		}
   480  		uri := protocol.URIFromPath(path)
   481  		if filterFunc(uri) {
   482  			return nil
   483  		}
   484  		// Get the file in order to include it in the snapshot.
   485  		// TODO(golang/go#57558): it is fundamentally broken to track files in this
   486  		// way; we may lose them if configuration or layout changes cause a view to
   487  		// be recreated.
   488  		//
   489  		// Furthermore, this operation must ignore errors, including context
   490  		// cancellation, or risk leaving the snapshot in an undefined state.
   491  		s.ReadFile(ctx, uri)
   492  		return nil
   493  	})
   494  	if err != nil {
   495  		event.Error(ctx, "searching for template files failed", err)
   496  	}
   497  }
   498  
   499  // filterFunc returns a func that reports whether uri is filtered by the currently configured
   500  // directoryFilters.
   501  func (v *View) filterFunc() func(protocol.DocumentURI) bool {
   502  	v.filterFuncOnce.Do(func() {
   503  		folderDir := v.folder.Dir.Path()
   504  		gomodcache := v.folder.Env.GOMODCACHE
   505  		var filters []string
   506  		filters = append(filters, v.folder.Options.DirectoryFilters...)
   507  		if pref := strings.TrimPrefix(gomodcache, folderDir); pref != gomodcache {
   508  			modcacheFilter := "-" + strings.TrimPrefix(filepath.ToSlash(pref), "/")
   509  			filters = append(filters, modcacheFilter)
   510  		}
   511  		filterer := NewFilterer(filters)
   512  		v._filterFunc = func(uri protocol.DocumentURI) bool {
   513  			// Only filter relative to the configured root directory.
   514  			if pathutil.InDir(folderDir, uri.Path()) {
   515  				return relPathExcludedByFilter(strings.TrimPrefix(uri.Path(), folderDir), filterer)
   516  			}
   517  			return false
   518  		}
   519  	})
   520  	return v._filterFunc
   521  }
   522  
   523  // shutdown releases resources associated with the view.
   524  func (v *View) shutdown() {
   525  	// Cancel the initial workspace load if it is still running.
   526  	v.cancelInitialWorkspaceLoad()
   527  
   528  	v.snapshotMu.Lock()
   529  	if v.snapshot != nil {
   530  		v.snapshot.cancel()
   531  		v.snapshot.decref()
   532  		v.snapshot = nil
   533  	}
   534  	v.snapshotMu.Unlock()
   535  }
   536  
   537  // IgnoredFile reports if a file would be ignored by a `go list` of the whole
   538  // workspace.
   539  //
   540  // While go list ./... skips directories starting with '.', '_', or 'testdata',
   541  // gopls may still load them via file queries. Explicitly filter them out.
   542  func (s *Snapshot) IgnoredFile(uri protocol.DocumentURI) bool {
   543  	// Fast path: if uri doesn't contain '.', '_', or 'testdata', it is not
   544  	// possible that it is ignored.
   545  	{
   546  		uriStr := string(uri)
   547  		if !strings.Contains(uriStr, ".") && !strings.Contains(uriStr, "_") && !strings.Contains(uriStr, "testdata") {
   548  			return false
   549  		}
   550  	}
   551  
   552  	return s.view.ignoreFilter.ignored(uri.Path())
   553  }
   554  
   555  // An ignoreFilter implements go list's exclusion rules via its 'ignored' method.
   556  type ignoreFilter struct {
   557  	prefixes []string // root dirs, ending in filepath.Separator
   558  }
   559  
   560  // newIgnoreFilter returns a new ignoreFilter implementing exclusion rules
   561  // relative to the provided directories.
   562  func newIgnoreFilter(dirs []string) *ignoreFilter {
   563  	f := new(ignoreFilter)
   564  	for _, d := range dirs {
   565  		f.prefixes = append(f.prefixes, filepath.Clean(d)+string(filepath.Separator))
   566  	}
   567  	return f
   568  }
   569  
   570  func (f *ignoreFilter) ignored(filename string) bool {
   571  	for _, prefix := range f.prefixes {
   572  		if suffix := strings.TrimPrefix(filename, prefix); suffix != filename {
   573  			if checkIgnored(suffix) {
   574  				return true
   575  			}
   576  		}
   577  	}
   578  	return false
   579  }
   580  
   581  // checkIgnored implements go list's exclusion rules.
   582  // Quoting “go help list”:
   583  //
   584  //	Directory and file names that begin with "." or "_" are ignored
   585  //	by the go tool, as are directories named "testdata".
   586  func checkIgnored(suffix string) bool {
   587  	// Note: this could be further optimized by writing a HasSegment helper, a
   588  	// segment-boundary respecting variant of strings.Contains.
   589  	for _, component := range strings.Split(suffix, string(filepath.Separator)) {
   590  		if len(component) == 0 {
   591  			continue
   592  		}
   593  		if component[0] == '.' || component[0] == '_' || component == "testdata" {
   594  			return true
   595  		}
   596  	}
   597  	return false
   598  }
   599  
   600  // Snapshot returns the current snapshot for the view, and a
   601  // release function that must be called when the Snapshot is
   602  // no longer needed.
   603  //
   604  // The resulting error is non-nil if and only if the view is shut down, in
   605  // which case the resulting release function will also be nil.
   606  func (v *View) Snapshot() (*Snapshot, func(), error) {
   607  	v.snapshotMu.Lock()
   608  	defer v.snapshotMu.Unlock()
   609  	if v.snapshot == nil {
   610  		return nil, nil, errors.New("view is shutdown")
   611  	}
   612  	return v.snapshot, v.snapshot.Acquire(), nil
   613  }
   614  
   615  // initialize loads the metadata (and currently, file contents, due to
   616  // golang/go#57558) for the main package query of the View, which depends on
   617  // the view type (see ViewType). If s.initialized is already true, initialize
   618  // is a no op.
   619  //
   620  // The first attempt--which populates the first snapshot for a new view--must
   621  // be allowed to run to completion without being cancelled.
   622  //
   623  // Subsequent attempts are triggered by conditions where gopls can't enumerate
   624  // specific packages that require reloading, such as a change to a go.mod file.
   625  // These attempts may be cancelled, and then retried by a later call.
   626  //
   627  // Postcondition: if ctx was not cancelled, s.initialized is true, s.initialErr
   628  // holds the error resulting from initialization, if any, and s.metadata holds
   629  // the resulting metadata graph.
   630  func (s *Snapshot) initialize(ctx context.Context, firstAttempt bool) {
   631  	// Acquire initializationSema, which is
   632  	// (in effect) a mutex with a timeout.
   633  	select {
   634  	case <-ctx.Done():
   635  		return
   636  	case s.view.initializationSema <- struct{}{}:
   637  	}
   638  
   639  	defer func() {
   640  		<-s.view.initializationSema
   641  	}()
   642  
   643  	s.mu.Lock()
   644  	initialized := s.initialized
   645  	s.mu.Unlock()
   646  
   647  	if initialized {
   648  		return
   649  	}
   650  
   651  	defer func() {
   652  		if firstAttempt {
   653  			close(s.view.initialWorkspaceLoad)
   654  		}
   655  	}()
   656  
   657  	// TODO(rFindley): we should only locate template files on the first attempt,
   658  	// or guard it via a different mechanism.
   659  	s.locateTemplateFiles(ctx)
   660  
   661  	// Collect module paths to load by parsing go.mod files. If a module fails to
   662  	// parse, capture the parsing failure as a critical diagnostic.
   663  	var scopes []loadScope           // scopes to load
   664  	var modDiagnostics []*Diagnostic // diagnostics for broken go.mod files
   665  	addError := func(uri protocol.DocumentURI, err error) {
   666  		modDiagnostics = append(modDiagnostics, &Diagnostic{
   667  			URI:      uri,
   668  			Severity: protocol.SeverityError,
   669  			Source:   ListError,
   670  			Message:  err.Error(),
   671  		})
   672  	}
   673  
   674  	if len(s.view.workspaceModFiles) > 0 {
   675  		for modURI := range s.view.workspaceModFiles {
   676  			// Verify that the modfile is valid before trying to load it.
   677  			//
   678  			// TODO(rfindley): now that we no longer need to parse the modfile in
   679  			// order to load scope, we could move these diagnostics to a more general
   680  			// location where we diagnose problems with modfiles or the workspace.
   681  			//
   682  			// Be careful not to add context cancellation errors as critical module
   683  			// errors.
   684  			fh, err := s.ReadFile(ctx, modURI)
   685  			if err != nil {
   686  				if ctx.Err() != nil {
   687  					return
   688  				}
   689  				addError(modURI, err)
   690  				continue
   691  			}
   692  			parsed, err := s.ParseMod(ctx, fh)
   693  			if err != nil {
   694  				if ctx.Err() != nil {
   695  					return
   696  				}
   697  				addError(modURI, err)
   698  				continue
   699  			}
   700  			if parsed.File == nil || parsed.File.Module == nil {
   701  				addError(modURI, fmt.Errorf("no module path for %s", modURI))
   702  				continue
   703  			}
   704  			moduleDir := filepath.Dir(modURI.Path())
   705  			// Previously, we loaded <modulepath>/... for each module path, but that
   706  			// is actually incorrect when the pattern may match packages in more than
   707  			// one module. See golang/go#59458 for more details.
   708  			scopes = append(scopes, moduleLoadScope{dir: moduleDir, modulePath: parsed.File.Module.Mod.Path})
   709  		}
   710  	} else {
   711  		scopes = append(scopes, viewLoadScope{})
   712  	}
   713  
   714  	// If we're loading anything, ensure we also load builtin,
   715  	// since it provides fake definitions (and documentation)
   716  	// for types like int that are used everywhere.
   717  	if len(scopes) > 0 {
   718  		scopes = append(scopes, packageLoadScope("builtin"))
   719  	}
   720  	loadErr := s.load(ctx, true, scopes...)
   721  
   722  	// A failure is retryable if it may have been due to context cancellation,
   723  	// and this is not the initial workspace load (firstAttempt==true).
   724  	//
   725  	// The IWL runs on a detached context with a long (~10m) timeout, so
   726  	// if the context was canceled we consider loading to have failed
   727  	// permanently.
   728  	if loadErr != nil && ctx.Err() != nil && !firstAttempt {
   729  		return
   730  	}
   731  
   732  	var initialErr *InitializationError
   733  	switch {
   734  	case loadErr != nil && ctx.Err() != nil:
   735  		event.Error(ctx, fmt.Sprintf("initial workspace load: %v", loadErr), loadErr)
   736  		initialErr = &InitializationError{
   737  			MainError: loadErr,
   738  		}
   739  	case loadErr != nil:
   740  		event.Error(ctx, "initial workspace load failed", loadErr)
   741  		extractedDiags := s.extractGoCommandErrors(ctx, loadErr)
   742  		initialErr = &InitializationError{
   743  			MainError:   loadErr,
   744  			Diagnostics: maps.Group(extractedDiags, byURI),
   745  		}
   746  	case s.view.workspaceModFilesErr != nil:
   747  		initialErr = &InitializationError{
   748  			MainError: s.view.workspaceModFilesErr,
   749  		}
   750  	case len(modDiagnostics) > 0:
   751  		initialErr = &InitializationError{
   752  			MainError: fmt.Errorf(modDiagnostics[0].Message),
   753  		}
   754  	}
   755  
   756  	s.mu.Lock()
   757  	defer s.mu.Unlock()
   758  
   759  	s.initialized = true
   760  	s.initialErr = initialErr
   761  }
   762  
   763  // A StateChange describes external state changes that may affect a snapshot.
   764  //
   765  // By far the most common of these is a change to file state, but a query of
   766  // module upgrade information or vulnerabilities also affects gopls' behavior.
   767  type StateChange struct {
   768  	Modifications  []file.Modification // if set, the raw modifications originating this change
   769  	Files          map[protocol.DocumentURI]file.Handle
   770  	ModuleUpgrades map[protocol.DocumentURI]map[string]string
   771  	Vulns          map[protocol.DocumentURI]*vulncheck.Result
   772  	GCDetails      map[metadata.PackageID]bool // package -> whether or not we want details
   773  }
   774  
   775  // InvalidateView processes the provided state change, invalidating any derived
   776  // results that depend on the changed state.
   777  //
   778  // The resulting snapshot is non-nil, representing the outcome of the state
   779  // change. The second result is a function that must be called to release the
   780  // snapshot when the snapshot is no longer needed.
   781  //
   782  // An error is returned if the given view is no longer active in the session.
   783  func (s *Session) InvalidateView(ctx context.Context, view *View, changed StateChange) (*Snapshot, func(), error) {
   784  	s.viewMu.Lock()
   785  	defer s.viewMu.Unlock()
   786  
   787  	if !slices.Contains(s.views, view) {
   788  		return nil, nil, fmt.Errorf("view is no longer active")
   789  	}
   790  	snapshot, release, _ := s.invalidateViewLocked(ctx, view, changed)
   791  	return snapshot, release, nil
   792  }
   793  
   794  // invalidateViewLocked invalidates the content of the given view.
   795  // (See [Session.InvalidateView]).
   796  //
   797  // The resulting bool reports whether the View needs to be re-diagnosed.
   798  // (See [Snapshot.clone]).
   799  //
   800  // s.viewMu must be held while calling this method.
   801  func (s *Session) invalidateViewLocked(ctx context.Context, v *View, changed StateChange) (*Snapshot, func(), bool) {
   802  	// Detach the context so that content invalidation cannot be canceled.
   803  	ctx = xcontext.Detach(ctx)
   804  
   805  	// This should be the only time we hold the view's snapshot lock for any period of time.
   806  	v.snapshotMu.Lock()
   807  	defer v.snapshotMu.Unlock()
   808  
   809  	prevSnapshot := v.snapshot
   810  
   811  	if prevSnapshot == nil {
   812  		panic("invalidateContent called after shutdown")
   813  	}
   814  
   815  	// Cancel all still-running previous requests, since they would be
   816  	// operating on stale data.
   817  	prevSnapshot.cancel()
   818  
   819  	// Do not clone a snapshot until its view has finished initializing.
   820  	//
   821  	// TODO(rfindley): shouldn't we do this before canceling?
   822  	prevSnapshot.AwaitInitialized(ctx)
   823  
   824  	var needsDiagnosis bool
   825  	s.snapshotWG.Add(1)
   826  	v.snapshot, needsDiagnosis = prevSnapshot.clone(ctx, v.baseCtx, changed, s.snapshotWG.Done)
   827  
   828  	// Remove the initial reference created when prevSnapshot was created.
   829  	prevSnapshot.decref()
   830  
   831  	// Return a second lease to the caller.
   832  	return v.snapshot, v.snapshot.Acquire(), needsDiagnosis
   833  }
   834  
   835  // defineView computes the view definition for the provided workspace folder
   836  // and URI.
   837  //
   838  // If forURI is non-empty, this view should be the best view including forURI.
   839  // Otherwise, it is the default view for the folder.
   840  //
   841  // defineView only returns an error in the event of context cancellation.
   842  //
   843  // Note: keep this function in sync with bestView.
   844  //
   845  // TODO(rfindley): we should be able to remove the error return, as
   846  // findModules is going away, and all other I/O is memoized.
   847  //
   848  // TODO(rfindley): pass in a narrower interface for the file.Source
   849  // (e.g. fileExists func(DocumentURI) bool) to make clear that this
   850  // process depends only on directory information, not file contents.
   851  func defineView(ctx context.Context, fs file.Source, folder *Folder, forFile file.Handle) (*viewDefinition, error) {
   852  	if err := checkPathValid(folder.Dir.Path()); err != nil {
   853  		return nil, fmt.Errorf("invalid workspace folder path: %w; check that the spelling of the configured workspace folder path agrees with the spelling reported by the operating system", err)
   854  	}
   855  	dir := folder.Dir.Path()
   856  	if forFile != nil {
   857  		dir = filepath.Dir(forFile.URI().Path())
   858  	}
   859  
   860  	def := new(viewDefinition)
   861  	def.folder = folder
   862  
   863  	if forFile != nil && fileKind(forFile) == file.Go {
   864  		// If the file has GOOS/GOARCH build constraints that
   865  		// don't match the folder's environment (which comes from
   866  		// 'go env' in the folder, plus user options),
   867  		// add those constraints to the viewDefinition's environment.
   868  
   869  		// Content trimming is nontrivial, so do this outside of the loop below.
   870  		// Keep this in sync with bestView.
   871  		path := forFile.URI().Path()
   872  		if content, err := forFile.Content(); err == nil {
   873  			// Note the err == nil condition above: by convention a non-existent file
   874  			// does not have any constraints. See the related note in bestView: this
   875  			// choice of behavior shouldn't actually matter. In this case, we should
   876  			// only call defineView with Overlays, which always have content.
   877  			content = trimContentForPortMatch(content)
   878  			viewPort := port{def.folder.Env.GOOS, def.folder.Env.GOARCH}
   879  			if !viewPort.matches(path, content) {
   880  				for _, p := range preferredPorts {
   881  					if p.matches(path, content) {
   882  						if def.envOverlay == nil {
   883  							def.envOverlay = make(map[string]string)
   884  						}
   885  						def.envOverlay["GOOS"] = p.GOOS
   886  						def.envOverlay["GOARCH"] = p.GOARCH
   887  						break
   888  					}
   889  				}
   890  			}
   891  		}
   892  	}
   893  
   894  	var err error
   895  	dirURI := protocol.URIFromPath(dir)
   896  	goworkFromEnv := false
   897  	if folder.Env.GOWORK != "off" && folder.Env.GOWORK != "" {
   898  		goworkFromEnv = true
   899  		def.gowork = protocol.URIFromPath(folder.Env.GOWORK)
   900  	} else {
   901  		def.gowork, err = findRootPattern(ctx, dirURI, "go.work", fs)
   902  		if err != nil {
   903  			return nil, err
   904  		}
   905  	}
   906  
   907  	// When deriving the best view for a given file, we only want to search
   908  	// up the directory hierarchy for modfiles.
   909  	def.gomod, err = findRootPattern(ctx, dirURI, "go.mod", fs)
   910  	if err != nil {
   911  		return nil, err
   912  	}
   913  
   914  	// Determine how we load and where to load package information for this view
   915  	//
   916  	// Specifically, set
   917  	//  - def.typ
   918  	//  - def.root
   919  	//  - def.workspaceModFiles, and
   920  	//  - def.envOverlay.
   921  
   922  	// If GOPACKAGESDRIVER is set it takes precedence.
   923  	{
   924  		// The value of GOPACKAGESDRIVER is not returned through the go command.
   925  		gopackagesdriver := os.Getenv("GOPACKAGESDRIVER")
   926  		// A user may also have a gopackagesdriver binary on their machine, which
   927  		// works the same way as setting GOPACKAGESDRIVER.
   928  		//
   929  		// TODO(rfindley): remove this call to LookPath. We should not support this
   930  		// undocumented method of setting GOPACKAGESDRIVER.
   931  		tool, err := exec.LookPath("gopackagesdriver")
   932  		if gopackagesdriver != "off" && (gopackagesdriver != "" || (err == nil && tool != "")) {
   933  			def.typ = GoPackagesDriverView
   934  			def.root = dirURI
   935  			return def, nil
   936  		}
   937  	}
   938  
   939  	// From go.dev/ref/mod, module mode is active if GO111MODULE=on, or
   940  	// GO111MODULE=auto or "" and we are inside a module or have a GOWORK value.
   941  	// But gopls is less strict, allowing GOPATH mode if GO111MODULE="", and
   942  	// AdHoc views if no module is found.
   943  
   944  	// gomodWorkspace is a helper to compute the correct set of workspace
   945  	// modfiles for a go.mod file, based on folder options.
   946  	gomodWorkspace := func() map[protocol.DocumentURI]unit {
   947  		modFiles := map[protocol.DocumentURI]struct{}{def.gomod: {}}
   948  		if folder.Options.IncludeReplaceInWorkspace {
   949  			includingReplace, err := goModModules(ctx, def.gomod, fs)
   950  			if err == nil {
   951  				modFiles = includingReplace
   952  			} else {
   953  				// If the go.mod file fails to parse, we don't know anything about
   954  				// replace directives, so fall back to a view of just the root module.
   955  			}
   956  		}
   957  		return modFiles
   958  	}
   959  
   960  	// Prefer a go.work file if it is available and contains the module relevant
   961  	// to forURI.
   962  	if def.adjustedGO111MODULE() != "off" && folder.Env.GOWORK != "off" && def.gowork != "" {
   963  		def.typ = GoWorkView
   964  		if goworkFromEnv {
   965  			// The go.work file could be anywhere, which can lead to confusing error
   966  			// messages.
   967  			def.root = dirURI
   968  		} else {
   969  			// The go.work file could be anywhere, which can lead to confusing error
   970  			def.root = def.gowork.Dir()
   971  		}
   972  		def.workspaceModFiles, def.workspaceModFilesErr = goWorkModules(ctx, def.gowork, fs)
   973  
   974  		// If forURI is in a module but that module is not
   975  		// included in the go.work file, use a go.mod view with GOWORK=off.
   976  		if forFile != nil && def.workspaceModFilesErr == nil && def.gomod != "" {
   977  			if _, ok := def.workspaceModFiles[def.gomod]; !ok {
   978  				def.typ = GoModView
   979  				def.root = def.gomod.Dir()
   980  				def.workspaceModFiles = gomodWorkspace()
   981  				if def.envOverlay == nil {
   982  					def.envOverlay = make(map[string]string)
   983  				}
   984  				def.envOverlay["GOWORK"] = "off"
   985  			}
   986  		}
   987  		return def, nil
   988  	}
   989  
   990  	// Otherwise, use the active module, if in module mode.
   991  	//
   992  	// Note, we could override GO111MODULE here via envOverlay if we wanted to
   993  	// support the case where someone opens a module with GO111MODULE=off. But
   994  	// that is probably not worth worrying about (at this point, folks probably
   995  	// shouldn't be setting GO111MODULE).
   996  	if def.adjustedGO111MODULE() != "off" && def.gomod != "" {
   997  		def.typ = GoModView
   998  		def.root = def.gomod.Dir()
   999  		def.workspaceModFiles = gomodWorkspace()
  1000  		return def, nil
  1001  	}
  1002  
  1003  	// Check if the workspace is within any GOPATH directory.
  1004  	inGOPATH := false
  1005  	for _, gp := range filepath.SplitList(folder.Env.GOPATH) {
  1006  		if pathutil.InDir(filepath.Join(gp, "src"), dir) {
  1007  			inGOPATH = true
  1008  			break
  1009  		}
  1010  	}
  1011  	if def.adjustedGO111MODULE() != "on" && inGOPATH {
  1012  		def.typ = GOPATHView
  1013  		def.root = dirURI
  1014  		return def, nil
  1015  	}
  1016  
  1017  	// We're not in a workspace, module, or GOPATH, so have no better choice than
  1018  	// an ad-hoc view.
  1019  	def.typ = AdHocView
  1020  	def.root = dirURI
  1021  	return def, nil
  1022  }
  1023  
  1024  // FetchGoEnv queries the environment and Go command to collect environment
  1025  // variables necessary for the workspace folder.
  1026  func FetchGoEnv(ctx context.Context, folder protocol.DocumentURI, opts *settings.Options) (*GoEnv, error) {
  1027  	dir := folder.Path()
  1028  	// All of the go commands invoked here should be fast. No need to share a
  1029  	// runner with other operations.
  1030  	runner := new(gocommand.Runner)
  1031  	inv := gocommand.Invocation{
  1032  		WorkingDir: dir,
  1033  		Env:        opts.EnvSlice(),
  1034  	}
  1035  
  1036  	var (
  1037  		env = new(GoEnv)
  1038  		err error
  1039  	)
  1040  	envvars := map[string]*string{
  1041  		"GOOS":        &env.GOOS,
  1042  		"GOARCH":      &env.GOARCH,
  1043  		"GOCACHE":     &env.GOCACHE,
  1044  		"GOPATH":      &env.GOPATH,
  1045  		"GOPRIVATE":   &env.GOPRIVATE,
  1046  		"GOMODCACHE":  &env.GOMODCACHE,
  1047  		"GOFLAGS":     &env.GOFLAGS,
  1048  		"GO111MODULE": &env.GO111MODULE,
  1049  	}
  1050  	if err := loadGoEnv(ctx, dir, opts.EnvSlice(), runner, envvars); err != nil {
  1051  		return nil, err
  1052  	}
  1053  
  1054  	env.GoVersion, err = gocommand.GoVersion(ctx, inv, runner)
  1055  	if err != nil {
  1056  		return nil, err
  1057  	}
  1058  	env.GoVersionOutput, err = gocommand.GoVersionOutput(ctx, inv, runner)
  1059  	if err != nil {
  1060  		return nil, err
  1061  	}
  1062  
  1063  	// The value of GOPACKAGESDRIVER is not returned through the go command.
  1064  	if driver, ok := opts.Env["GOPACKAGESDRIVER"]; ok {
  1065  		env.GOPACKAGESDRIVER = driver
  1066  	} else {
  1067  		env.GOPACKAGESDRIVER = os.Getenv("GOPACKAGESDRIVER")
  1068  		// A user may also have a gopackagesdriver binary on their machine, which
  1069  		// works the same way as setting GOPACKAGESDRIVER.
  1070  		//
  1071  		// TODO(rfindley): remove this call to LookPath. We should not support this
  1072  		// undocumented method of setting GOPACKAGESDRIVER.
  1073  		if env.GOPACKAGESDRIVER == "" {
  1074  			tool, err := exec.LookPath("gopackagesdriver")
  1075  			if err == nil && tool != "" {
  1076  				env.GOPACKAGESDRIVER = tool
  1077  			}
  1078  		}
  1079  	}
  1080  
  1081  	// While GOWORK is available through the Go command, we want to differentiate
  1082  	// between an explicit GOWORK value and one which is implicit from the file
  1083  	// system. The former doesn't change unless the environment changes.
  1084  	if gowork, ok := opts.Env["GOWORK"]; ok {
  1085  		env.GOWORK = gowork
  1086  	} else {
  1087  		env.GOWORK = os.Getenv("GOWORK")
  1088  	}
  1089  	return env, nil
  1090  }
  1091  
  1092  // loadGoEnv loads `go env` values into the provided map, keyed by Go variable
  1093  // name.
  1094  func loadGoEnv(ctx context.Context, dir string, configEnv []string, runner *gocommand.Runner, vars map[string]*string) error {
  1095  	// We can save ~200 ms by requesting only the variables we care about.
  1096  	args := []string{"-json"}
  1097  	for k := range vars {
  1098  		args = append(args, k)
  1099  	}
  1100  
  1101  	inv := gocommand.Invocation{
  1102  		Verb:       "env",
  1103  		Args:       args,
  1104  		Env:        configEnv,
  1105  		WorkingDir: dir,
  1106  	}
  1107  	stdout, err := runner.Run(ctx, inv)
  1108  	if err != nil {
  1109  		return err
  1110  	}
  1111  	envMap := make(map[string]string)
  1112  	if err := json.Unmarshal(stdout.Bytes(), &envMap); err != nil {
  1113  		return fmt.Errorf("internal error unmarshaling JSON from 'go env': %w", err)
  1114  	}
  1115  	for key, ptr := range vars {
  1116  		*ptr = envMap[key]
  1117  	}
  1118  
  1119  	return nil
  1120  }
  1121  
  1122  // findRootPattern looks for files with the given basename in dir or any parent
  1123  // directory of dir, using the provided FileSource. It returns the first match,
  1124  // starting from dir and search parents.
  1125  //
  1126  // The resulting string is either the file path of a matching file with the
  1127  // given basename, or "" if none was found.
  1128  //
  1129  // findRootPattern only returns an error in the case of context cancellation.
  1130  func findRootPattern(ctx context.Context, dirURI protocol.DocumentURI, basename string, fs file.Source) (protocol.DocumentURI, error) {
  1131  	dir := dirURI.Path()
  1132  	for dir != "" {
  1133  		target := filepath.Join(dir, basename)
  1134  		uri := protocol.URIFromPath(target)
  1135  		fh, err := fs.ReadFile(ctx, uri)
  1136  		if err != nil {
  1137  			return "", err // context cancelled
  1138  		}
  1139  		if fileExists(fh) {
  1140  			return uri, nil
  1141  		}
  1142  		// Trailing separators must be trimmed, otherwise filepath.Split is a noop.
  1143  		next, _ := filepath.Split(strings.TrimRight(dir, string(filepath.Separator)))
  1144  		if next == dir {
  1145  			break
  1146  		}
  1147  		dir = next
  1148  	}
  1149  	return "", nil
  1150  }
  1151  
  1152  // checkPathValid performs an OS-specific path validity check. The
  1153  // implementation varies for filesystems that are case-insensitive
  1154  // (e.g. macOS, Windows), and for those that disallow certain file
  1155  // names (e.g. path segments ending with a period on Windows, or
  1156  // reserved names such as "com"; see
  1157  // https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file).
  1158  var checkPathValid = defaultCheckPathValid
  1159  
  1160  // CheckPathValid checks whether a directory is suitable as a workspace folder.
  1161  func CheckPathValid(dir string) error { return checkPathValid(dir) }
  1162  
  1163  func defaultCheckPathValid(path string) error {
  1164  	return nil
  1165  }
  1166  
  1167  // IsGoPrivatePath reports whether target is a private import path, as identified
  1168  // by the GOPRIVATE environment variable.
  1169  func (s *Snapshot) IsGoPrivatePath(target string) bool {
  1170  	return globsMatchPath(s.view.folder.Env.GOPRIVATE, target)
  1171  }
  1172  
  1173  // ModuleUpgrades returns known module upgrades for the dependencies of
  1174  // modfile.
  1175  func (s *Snapshot) ModuleUpgrades(modfile protocol.DocumentURI) map[string]string {
  1176  	s.mu.Lock()
  1177  	defer s.mu.Unlock()
  1178  	upgrades := map[string]string{}
  1179  	orig, _ := s.moduleUpgrades.Get(modfile)
  1180  	for mod, ver := range orig {
  1181  		upgrades[mod] = ver
  1182  	}
  1183  	return upgrades
  1184  }
  1185  
  1186  // MaxGovulncheckResultsAge defines the maximum vulnerability age considered
  1187  // valid by gopls.
  1188  //
  1189  // Mutable for testing.
  1190  var MaxGovulncheckResultAge = 1 * time.Hour
  1191  
  1192  // Vulnerabilities returns known vulnerabilities for the given modfile.
  1193  //
  1194  // Results more than an hour old are excluded.
  1195  //
  1196  // TODO(suzmue): replace command.Vuln with a different type, maybe
  1197  // https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck/govulnchecklib#Summary?
  1198  //
  1199  // TODO(rfindley): move to snapshot.go
  1200  func (s *Snapshot) Vulnerabilities(modfiles ...protocol.DocumentURI) map[protocol.DocumentURI]*vulncheck.Result {
  1201  	m := make(map[protocol.DocumentURI]*vulncheck.Result)
  1202  	now := time.Now()
  1203  
  1204  	s.mu.Lock()
  1205  	defer s.mu.Unlock()
  1206  
  1207  	if len(modfiles) == 0 { // empty means all modfiles
  1208  		modfiles = s.vulns.Keys()
  1209  	}
  1210  	for _, modfile := range modfiles {
  1211  		vuln, _ := s.vulns.Get(modfile)
  1212  		if vuln != nil && now.Sub(vuln.AsOf) > MaxGovulncheckResultAge {
  1213  			vuln = nil
  1214  		}
  1215  		m[modfile] = vuln
  1216  	}
  1217  	return m
  1218  }
  1219  
  1220  // GoVersion returns the effective release Go version (the X in go1.X) for this
  1221  // view.
  1222  func (v *View) GoVersion() int {
  1223  	return v.folder.Env.GoVersion
  1224  }
  1225  
  1226  // GoVersionString returns the effective Go version string for this view.
  1227  //
  1228  // Unlike [GoVersion], this encodes the minor version and commit hash information.
  1229  func (v *View) GoVersionString() string {
  1230  	return gocommand.ParseGoVersionOutput(v.folder.Env.GoVersionOutput)
  1231  }
  1232  
  1233  // GoVersionString is temporarily available from the snapshot.
  1234  //
  1235  // TODO(rfindley): refactor so that this method is not necessary.
  1236  func (s *Snapshot) GoVersionString() string {
  1237  	return s.view.GoVersionString()
  1238  }
  1239  
  1240  // Copied from
  1241  // https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/str/path.go;l=58;drc=2910c5b4a01a573ebc97744890a07c1a3122c67a
  1242  func globsMatchPath(globs, target string) bool {
  1243  	for globs != "" {
  1244  		// Extract next non-empty glob in comma-separated list.
  1245  		var glob string
  1246  		if i := strings.Index(globs, ","); i >= 0 {
  1247  			glob, globs = globs[:i], globs[i+1:]
  1248  		} else {
  1249  			glob, globs = globs, ""
  1250  		}
  1251  		if glob == "" {
  1252  			continue
  1253  		}
  1254  
  1255  		// A glob with N+1 path elements (N slashes) needs to be matched
  1256  		// against the first N+1 path elements of target,
  1257  		// which end just before the N+1'th slash.
  1258  		n := strings.Count(glob, "/")
  1259  		prefix := target
  1260  		// Walk target, counting slashes, truncating at the N+1'th slash.
  1261  		for i := 0; i < len(target); i++ {
  1262  			if target[i] == '/' {
  1263  				if n == 0 {
  1264  					prefix = target[:i]
  1265  					break
  1266  				}
  1267  				n--
  1268  			}
  1269  		}
  1270  		if n > 0 {
  1271  			// Not enough prefix elements.
  1272  			continue
  1273  		}
  1274  		matched, _ := path.Match(glob, prefix)
  1275  		if matched {
  1276  			return true
  1277  		}
  1278  	}
  1279  	return false
  1280  }
  1281  
  1282  var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
  1283  
  1284  // TODO(rfindley): clean up the redundancy of allFilesExcluded,
  1285  // pathExcludedByFilterFunc, pathExcludedByFilter, view.filterFunc...
  1286  func allFilesExcluded(files []string, filterFunc func(protocol.DocumentURI) bool) bool {
  1287  	for _, f := range files {
  1288  		uri := protocol.URIFromPath(f)
  1289  		if !filterFunc(uri) {
  1290  			return false
  1291  		}
  1292  	}
  1293  	return true
  1294  }
  1295  
  1296  func relPathExcludedByFilter(path string, filterer *Filterer) bool {
  1297  	path = strings.TrimPrefix(filepath.ToSlash(path), "/")
  1298  	return filterer.Disallow(path)
  1299  }