github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/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 implements the caching layer for gopls.
     6  package cache
     7  
     8  import (
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"os"
    15  	"path"
    16  	"path/filepath"
    17  	"reflect"
    18  	"regexp"
    19  	"sort"
    20  	"strings"
    21  	"sync"
    22  
    23  	"golang.org/x/mod/modfile"
    24  	"golang.org/x/mod/semver"
    25  	exec "golang.org/x/sys/execabs"
    26  	"github.com/powerman/golang-tools/go/packages"
    27  	"github.com/powerman/golang-tools/internal/event"
    28  	"github.com/powerman/golang-tools/internal/gocommand"
    29  	"github.com/powerman/golang-tools/internal/imports"
    30  	"github.com/powerman/golang-tools/internal/lsp/protocol"
    31  	"github.com/powerman/golang-tools/internal/lsp/source"
    32  	"github.com/powerman/golang-tools/internal/span"
    33  	"github.com/powerman/golang-tools/internal/xcontext"
    34  	errors "golang.org/x/xerrors"
    35  )
    36  
    37  type View struct {
    38  	session *Session
    39  	id      string
    40  
    41  	optionsMu sync.Mutex
    42  	options   *source.Options
    43  
    44  	// mu protects most mutable state of the view.
    45  	mu sync.Mutex
    46  
    47  	// baseCtx is the context handed to NewView. This is the parent of all
    48  	// background contexts created for this view.
    49  	baseCtx context.Context
    50  
    51  	// cancel is called when all action being performed by the current view
    52  	// should be stopped.
    53  	cancel context.CancelFunc
    54  
    55  	// name is the user visible name of this view.
    56  	name string
    57  
    58  	// folder is the folder with which this view was constructed.
    59  	folder span.URI
    60  
    61  	importsState *importsState
    62  
    63  	// moduleUpgrades tracks known upgrades for module paths.
    64  	moduleUpgrades map[string]string
    65  
    66  	// keep track of files by uri and by basename, a single file may be mapped
    67  	// to multiple uris, and the same basename may map to multiple files
    68  	filesByURI  map[span.URI]*fileBase
    69  	filesByBase map[string][]*fileBase
    70  
    71  	// initCancelFirstAttempt can be used to terminate the view's first
    72  	// attempt at initialization.
    73  	initCancelFirstAttempt context.CancelFunc
    74  
    75  	snapshotMu sync.Mutex
    76  	snapshot   *snapshot // nil after shutdown has been called
    77  
    78  	// initialWorkspaceLoad is closed when the first workspace initialization has
    79  	// completed. If we failed to load, we only retry if the go.mod file changes,
    80  	// to avoid too many go/packages calls.
    81  	initialWorkspaceLoad chan struct{}
    82  
    83  	// initializationSema is used limit concurrent initialization of snapshots in
    84  	// the view. We use a channel instead of a mutex to avoid blocking when a
    85  	// context is canceled.
    86  	initializationSema chan struct{}
    87  
    88  	// rootURI is the rootURI directory of this view. If we are in GOPATH mode, this
    89  	// is just the folder. If we are in module mode, this is the module rootURI.
    90  	rootURI span.URI
    91  
    92  	// workspaceInformation tracks various details about this view's
    93  	// environment variables, go version, and use of modules.
    94  	workspaceInformation
    95  }
    96  
    97  type workspaceInformation struct {
    98  	// The Go version in use: X in Go 1.X.
    99  	goversion int
   100  
   101  	// hasGopackagesDriver is true if the user has a value set for the
   102  	// GOPACKAGESDRIVER environment variable or a gopackagesdriver binary on
   103  	// their machine.
   104  	hasGopackagesDriver bool
   105  
   106  	// `go env` variables that need to be tracked by gopls.
   107  	environmentVariables
   108  
   109  	// userGo111Module is the user's value of GO111MODULE.
   110  	userGo111Module go111module
   111  
   112  	// The value of GO111MODULE we want to run with.
   113  	effectiveGo111Module string
   114  
   115  	// goEnv is the `go env` output collected when a view is created.
   116  	// It includes the values of the environment variables above.
   117  	goEnv map[string]string
   118  }
   119  
   120  type go111module int
   121  
   122  const (
   123  	off = go111module(iota)
   124  	auto
   125  	on
   126  )
   127  
   128  type environmentVariables struct {
   129  	gocache, gopath, goroot, goprivate, gomodcache, go111module string
   130  }
   131  
   132  type workspaceMode int
   133  
   134  const (
   135  	moduleMode workspaceMode = 1 << iota
   136  
   137  	// tempModfile indicates whether or not the -modfile flag should be used.
   138  	tempModfile
   139  )
   140  
   141  // fileBase holds the common functionality for all files.
   142  // It is intended to be embedded in the file implementations
   143  type fileBase struct {
   144  	uris  []span.URI
   145  	fname string
   146  
   147  	view *View
   148  }
   149  
   150  func (f *fileBase) URI() span.URI {
   151  	return f.uris[0]
   152  }
   153  
   154  func (f *fileBase) filename() string {
   155  	return f.fname
   156  }
   157  
   158  func (f *fileBase) addURI(uri span.URI) int {
   159  	f.uris = append(f.uris, uri)
   160  	return len(f.uris)
   161  }
   162  
   163  func (v *View) ID() string { return v.id }
   164  
   165  // tempModFile creates a temporary go.mod file based on the contents of the
   166  // given go.mod file. It is the caller's responsibility to clean up the files
   167  // when they are done using them.
   168  func tempModFile(modFh source.FileHandle, gosum []byte) (tmpURI span.URI, cleanup func(), err error) {
   169  	filenameHash := hashContents([]byte(modFh.URI().Filename()))
   170  	tmpMod, err := ioutil.TempFile("", fmt.Sprintf("go.%s.*.mod", filenameHash))
   171  	if err != nil {
   172  		return "", nil, err
   173  	}
   174  	defer tmpMod.Close()
   175  
   176  	tmpURI = span.URIFromPath(tmpMod.Name())
   177  	tmpSumName := sumFilename(tmpURI)
   178  
   179  	content, err := modFh.Read()
   180  	if err != nil {
   181  		return "", nil, err
   182  	}
   183  
   184  	if _, err := tmpMod.Write(content); err != nil {
   185  		return "", nil, err
   186  	}
   187  
   188  	cleanup = func() {
   189  		_ = os.Remove(tmpSumName)
   190  		_ = os.Remove(tmpURI.Filename())
   191  	}
   192  
   193  	// Be careful to clean up if we return an error from this function.
   194  	defer func() {
   195  		if err != nil {
   196  			cleanup()
   197  			cleanup = nil
   198  		}
   199  	}()
   200  
   201  	// Create an analogous go.sum, if one exists.
   202  	if gosum != nil {
   203  		if err := ioutil.WriteFile(tmpSumName, gosum, 0655); err != nil {
   204  			return "", cleanup, err
   205  		}
   206  	}
   207  
   208  	return tmpURI, cleanup, nil
   209  }
   210  
   211  // Name returns the user visible name of this view.
   212  func (v *View) Name() string {
   213  	return v.name
   214  }
   215  
   216  // Folder returns the folder at the base of this view.
   217  func (v *View) Folder() span.URI {
   218  	return v.folder
   219  }
   220  
   221  func (v *View) Options() *source.Options {
   222  	v.optionsMu.Lock()
   223  	defer v.optionsMu.Unlock()
   224  	return v.options
   225  }
   226  
   227  func (v *View) FileKind(fh source.FileHandle) source.FileKind {
   228  	if o, ok := fh.(source.Overlay); ok {
   229  		if o.Kind() != source.UnknownKind {
   230  			return o.Kind()
   231  		}
   232  	}
   233  	fext := filepath.Ext(fh.URI().Filename())
   234  	switch fext {
   235  	case ".go":
   236  		return source.Go
   237  	case ".mod":
   238  		return source.Mod
   239  	case ".sum":
   240  		return source.Sum
   241  	case ".work":
   242  		return source.Work
   243  	}
   244  	exts := v.Options().TemplateExtensions
   245  	for _, ext := range exts {
   246  		if fext == ext || fext == "."+ext {
   247  			return source.Tmpl
   248  		}
   249  	}
   250  	// and now what? This should never happen, but it does for cgo before go1.15
   251  	return source.Go
   252  }
   253  
   254  func minorOptionsChange(a, b *source.Options) bool {
   255  	// Check if any of the settings that modify our understanding of files have been changed
   256  	if !reflect.DeepEqual(a.Env, b.Env) {
   257  		return false
   258  	}
   259  	if !reflect.DeepEqual(a.DirectoryFilters, b.DirectoryFilters) {
   260  		return false
   261  	}
   262  	if a.MemoryMode != b.MemoryMode {
   263  		return false
   264  	}
   265  	aBuildFlags := make([]string, len(a.BuildFlags))
   266  	bBuildFlags := make([]string, len(b.BuildFlags))
   267  	copy(aBuildFlags, a.BuildFlags)
   268  	copy(bBuildFlags, b.BuildFlags)
   269  	sort.Strings(aBuildFlags)
   270  	sort.Strings(bBuildFlags)
   271  	// the rest of the options are benign
   272  	return reflect.DeepEqual(aBuildFlags, bBuildFlags)
   273  }
   274  
   275  func (v *View) SetOptions(ctx context.Context, options *source.Options) (source.View, error) {
   276  	// no need to rebuild the view if the options were not materially changed
   277  	v.optionsMu.Lock()
   278  	if minorOptionsChange(v.options, options) {
   279  		v.options = options
   280  		v.optionsMu.Unlock()
   281  		return v, nil
   282  	}
   283  	v.optionsMu.Unlock()
   284  	newView, err := v.session.updateView(ctx, v, options)
   285  	return newView, err
   286  }
   287  
   288  func (v *View) Rebuild(ctx context.Context) (source.Snapshot, func(), error) {
   289  	newView, err := v.session.updateView(ctx, v, v.Options())
   290  	if err != nil {
   291  		return nil, func() {}, err
   292  	}
   293  	snapshot, release := newView.Snapshot(ctx)
   294  	return snapshot, release, nil
   295  }
   296  
   297  func (s *snapshot) WriteEnv(ctx context.Context, w io.Writer) error {
   298  	s.view.optionsMu.Lock()
   299  	env := s.view.options.EnvSlice()
   300  	buildFlags := append([]string{}, s.view.options.BuildFlags...)
   301  	s.view.optionsMu.Unlock()
   302  
   303  	fullEnv := make(map[string]string)
   304  	for k, v := range s.view.goEnv {
   305  		fullEnv[k] = v
   306  	}
   307  	for _, v := range env {
   308  		s := strings.SplitN(v, "=", 2)
   309  		if len(s) != 2 {
   310  			continue
   311  		}
   312  		if _, ok := fullEnv[s[0]]; ok {
   313  			fullEnv[s[0]] = s[1]
   314  		}
   315  	}
   316  	goVersion, err := s.view.session.gocmdRunner.Run(ctx, gocommand.Invocation{
   317  		Verb:       "version",
   318  		Env:        env,
   319  		WorkingDir: s.view.rootURI.Filename(),
   320  	})
   321  	if err != nil {
   322  		return err
   323  	}
   324  	fmt.Fprintf(w, `go env for %v
   325  (root %s)
   326  (go version %s)
   327  (valid build configuration = %v)
   328  (build flags: %v)
   329  `,
   330  		s.view.folder.Filename(),
   331  		s.view.rootURI.Filename(),
   332  		strings.TrimRight(goVersion.String(), "\n"),
   333  		s.ValidBuildConfiguration(),
   334  		buildFlags)
   335  	for k, v := range fullEnv {
   336  		fmt.Fprintf(w, "%s=%s\n", k, v)
   337  	}
   338  	return nil
   339  }
   340  
   341  func (s *snapshot) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error {
   342  	return s.view.importsState.runProcessEnvFunc(ctx, s, fn)
   343  }
   344  
   345  // separated out from its sole use in locateTemplateFiles for testability
   346  func fileHasExtension(path string, suffixes []string) bool {
   347  	ext := filepath.Ext(path)
   348  	if ext != "" && ext[0] == '.' {
   349  		ext = ext[1:]
   350  	}
   351  	for _, s := range suffixes {
   352  		if s != "" && ext == s {
   353  			return true
   354  		}
   355  	}
   356  	return false
   357  }
   358  
   359  func (s *snapshot) locateTemplateFiles(ctx context.Context) {
   360  	if len(s.view.Options().TemplateExtensions) == 0 {
   361  		return
   362  	}
   363  	suffixes := s.view.Options().TemplateExtensions
   364  
   365  	// The workspace root may have been expanded to a module, but we should apply
   366  	// directory filters based on the configured workspace folder.
   367  	//
   368  	// TODO(rfindley): we should be more principled about paths outside of the
   369  	// workspace folder: do we even consider them? Do we support absolute
   370  	// exclusions? Relative exclusions starting with ..?
   371  	dir := s.workspace.root.Filename()
   372  	relativeTo := s.view.folder.Filename()
   373  
   374  	searched := 0
   375  	// Change to WalkDir when we move up to 1.16
   376  	err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
   377  		if err != nil {
   378  			return err
   379  		}
   380  		relpath := strings.TrimPrefix(path, relativeTo)
   381  		excluded := pathExcludedByFilter(relpath, dir, s.view.gomodcache, s.view.options)
   382  		if fileHasExtension(path, suffixes) && !excluded && !fi.IsDir() {
   383  			k := span.URIFromPath(path)
   384  			_, err := s.GetVersionedFile(ctx, k)
   385  			if err != nil {
   386  				return nil
   387  			}
   388  		}
   389  		searched++
   390  		if fileLimit > 0 && searched > fileLimit {
   391  			return errExhausted
   392  		}
   393  		return nil
   394  	})
   395  	if err != nil {
   396  		event.Error(ctx, "searching for template files failed", err)
   397  	}
   398  }
   399  
   400  func (v *View) contains(uri span.URI) bool {
   401  	inRoot := source.InDir(v.rootURI.Filename(), uri.Filename())
   402  	inFolder := source.InDir(v.folder.Filename(), uri.Filename())
   403  	if !inRoot && !inFolder {
   404  		return false
   405  	}
   406  	// Filters are applied relative to the workspace folder.
   407  	if inFolder {
   408  		return !pathExcludedByFilter(strings.TrimPrefix(uri.Filename(), v.folder.Filename()), v.rootURI.Filename(), v.gomodcache, v.Options())
   409  	}
   410  	return true
   411  }
   412  
   413  func (v *View) mapFile(uri span.URI, f *fileBase) {
   414  	v.filesByURI[uri] = f
   415  	if f.addURI(uri) == 1 {
   416  		basename := basename(f.filename())
   417  		v.filesByBase[basename] = append(v.filesByBase[basename], f)
   418  	}
   419  }
   420  
   421  func basename(filename string) string {
   422  	return strings.ToLower(filepath.Base(filename))
   423  }
   424  
   425  func (v *View) relevantChange(c source.FileModification) bool {
   426  	// If the file is known to the view, the change is relevant.
   427  	if v.knownFile(c.URI) {
   428  		return true
   429  	}
   430  	// The go.work/gopls.mod may not be "known" because we first access it
   431  	// through the session. As a result, treat changes to the view's go.work or
   432  	// gopls.mod file as always relevant, even if they are only on-disk
   433  	// changes.
   434  	// TODO(rstambler): Make sure the go.work/gopls.mod files are always known
   435  	// to the view.
   436  	for _, src := range []workspaceSource{goWorkWorkspace, goplsModWorkspace} {
   437  		if c.URI == uriForSource(v.rootURI, src) {
   438  			return true
   439  		}
   440  	}
   441  	// If the file is not known to the view, and the change is only on-disk,
   442  	// we should not invalidate the snapshot. This is necessary because Emacs
   443  	// sends didChangeWatchedFiles events for temp files.
   444  	if c.OnDisk && (c.Action == source.Change || c.Action == source.Delete) {
   445  		return false
   446  	}
   447  	return v.contains(c.URI)
   448  }
   449  
   450  func (v *View) knownFile(uri span.URI) bool {
   451  	v.mu.Lock()
   452  	defer v.mu.Unlock()
   453  
   454  	f, err := v.findFile(uri)
   455  	return f != nil && err == nil
   456  }
   457  
   458  // getFile returns a file for the given URI.
   459  func (v *View) getFile(uri span.URI) *fileBase {
   460  	v.mu.Lock()
   461  	defer v.mu.Unlock()
   462  
   463  	f, _ := v.findFile(uri)
   464  	if f != nil {
   465  		return f
   466  	}
   467  	f = &fileBase{
   468  		view:  v,
   469  		fname: uri.Filename(),
   470  	}
   471  	v.mapFile(uri, f)
   472  	return f
   473  }
   474  
   475  // findFile checks the cache for any file matching the given uri.
   476  //
   477  // An error is only returned for an irreparable failure, for example, if the
   478  // filename in question does not exist.
   479  func (v *View) findFile(uri span.URI) (*fileBase, error) {
   480  	if f := v.filesByURI[uri]; f != nil {
   481  		// a perfect match
   482  		return f, nil
   483  	}
   484  	// no exact match stored, time to do some real work
   485  	// check for any files with the same basename
   486  	fname := uri.Filename()
   487  	basename := basename(fname)
   488  	if candidates := v.filesByBase[basename]; candidates != nil {
   489  		pathStat, err := os.Stat(fname)
   490  		if os.IsNotExist(err) {
   491  			return nil, err
   492  		}
   493  		if err != nil {
   494  			return nil, nil // the file may exist, return without an error
   495  		}
   496  		for _, c := range candidates {
   497  			if cStat, err := os.Stat(c.filename()); err == nil {
   498  				if os.SameFile(pathStat, cStat) {
   499  					// same file, map it
   500  					v.mapFile(uri, c)
   501  					return c, nil
   502  				}
   503  			}
   504  		}
   505  	}
   506  	// no file with a matching name was found, it wasn't in our cache
   507  	return nil, nil
   508  }
   509  
   510  func (v *View) Shutdown(ctx context.Context) {
   511  	v.session.removeView(ctx, v)
   512  }
   513  
   514  // TODO(rFindley): probably some of this should also be one in View.Shutdown
   515  // above?
   516  func (v *View) shutdown(ctx context.Context) {
   517  	// Cancel the initial workspace load if it is still running.
   518  	v.initCancelFirstAttempt()
   519  
   520  	v.mu.Lock()
   521  	if v.cancel != nil {
   522  		v.cancel()
   523  		v.cancel = nil
   524  	}
   525  	v.mu.Unlock()
   526  	v.snapshotMu.Lock()
   527  	if v.snapshot != nil {
   528  		go v.snapshot.generation.Destroy("View.shutdown")
   529  		v.snapshot = nil
   530  	}
   531  	v.snapshotMu.Unlock()
   532  	v.importsState.destroy()
   533  }
   534  
   535  func (v *View) Session() *Session {
   536  	return v.session
   537  }
   538  
   539  func (s *snapshot) IgnoredFile(uri span.URI) bool {
   540  	filename := uri.Filename()
   541  	var prefixes []string
   542  	if len(s.workspace.getActiveModFiles()) == 0 {
   543  		for _, entry := range filepath.SplitList(s.view.gopath) {
   544  			prefixes = append(prefixes, filepath.Join(entry, "src"))
   545  		}
   546  	} else {
   547  		prefixes = append(prefixes, s.view.gomodcache)
   548  		for m := range s.workspace.getActiveModFiles() {
   549  			prefixes = append(prefixes, dirURI(m).Filename())
   550  		}
   551  	}
   552  	for _, prefix := range prefixes {
   553  		if strings.HasPrefix(filename, prefix) {
   554  			return checkIgnored(filename[len(prefix):])
   555  		}
   556  	}
   557  	return false
   558  }
   559  
   560  // checkIgnored implements go list's exclusion rules. go help list:
   561  // 		Directory and file names that begin with "." or "_" are ignored
   562  // 		by the go tool, as are directories named "testdata".
   563  func checkIgnored(suffix string) bool {
   564  	for _, component := range strings.Split(suffix, string(filepath.Separator)) {
   565  		if len(component) == 0 {
   566  			continue
   567  		}
   568  		if component[0] == '.' || component[0] == '_' || component == "testdata" {
   569  			return true
   570  		}
   571  	}
   572  	return false
   573  }
   574  
   575  func (v *View) Snapshot(ctx context.Context) (source.Snapshot, func()) {
   576  	return v.getSnapshot()
   577  }
   578  
   579  func (v *View) getSnapshot() (*snapshot, func()) {
   580  	v.snapshotMu.Lock()
   581  	defer v.snapshotMu.Unlock()
   582  	if v.snapshot == nil {
   583  		panic("getSnapshot called after shutdown")
   584  	}
   585  	return v.snapshot, v.snapshot.generation.Acquire()
   586  }
   587  
   588  func (s *snapshot) initialize(ctx context.Context, firstAttempt bool) {
   589  	select {
   590  	case <-ctx.Done():
   591  		return
   592  	case s.view.initializationSema <- struct{}{}:
   593  	}
   594  
   595  	defer func() {
   596  		<-s.view.initializationSema
   597  	}()
   598  
   599  	if s.initializeOnce == nil {
   600  		return
   601  	}
   602  	s.initializeOnce.Do(func() {
   603  		s.loadWorkspace(ctx, firstAttempt)
   604  		s.collectAllKnownSubdirs(ctx)
   605  	})
   606  }
   607  
   608  func (s *snapshot) loadWorkspace(ctx context.Context, firstAttempt bool) {
   609  	defer func() {
   610  		s.initializeOnce = nil
   611  		if firstAttempt {
   612  			close(s.view.initialWorkspaceLoad)
   613  		}
   614  	}()
   615  
   616  	// If we have multiple modules, we need to load them by paths.
   617  	var scopes []interface{}
   618  	var modDiagnostics []*source.Diagnostic
   619  	addError := func(uri span.URI, err error) {
   620  		modDiagnostics = append(modDiagnostics, &source.Diagnostic{
   621  			URI:      uri,
   622  			Severity: protocol.SeverityError,
   623  			Source:   source.ListError,
   624  			Message:  err.Error(),
   625  		})
   626  	}
   627  	s.locateTemplateFiles(ctx)
   628  	if len(s.workspace.getActiveModFiles()) > 0 {
   629  		for modURI := range s.workspace.getActiveModFiles() {
   630  			fh, err := s.GetFile(ctx, modURI)
   631  			if err != nil {
   632  				addError(modURI, err)
   633  				continue
   634  			}
   635  			parsed, err := s.ParseMod(ctx, fh)
   636  			if err != nil {
   637  				addError(modURI, err)
   638  				continue
   639  			}
   640  			if parsed.File == nil || parsed.File.Module == nil {
   641  				addError(modURI, fmt.Errorf("no module path for %s", modURI))
   642  				continue
   643  			}
   644  			path := parsed.File.Module.Mod.Path
   645  			scopes = append(scopes, moduleLoadScope(path))
   646  		}
   647  	} else {
   648  		scopes = append(scopes, viewLoadScope("LOAD_VIEW"))
   649  	}
   650  
   651  	// If we're loading anything, ensure we also load builtin.
   652  	// TODO(rstambler): explain the rationale for this.
   653  	if len(scopes) > 0 {
   654  		scopes = append(scopes, PackagePath("builtin"))
   655  	}
   656  	err := s.load(ctx, firstAttempt, scopes...)
   657  
   658  	// If the context is canceled on the first attempt, loading has failed
   659  	// because the go command has timed out--that should be a critical error.
   660  	if err != nil && !firstAttempt && ctx.Err() != nil {
   661  		return
   662  	}
   663  
   664  	var criticalErr *source.CriticalError
   665  	switch {
   666  	case err != nil && ctx.Err() != nil:
   667  		event.Error(ctx, fmt.Sprintf("initial workspace load: %v", err), err)
   668  		criticalErr = &source.CriticalError{
   669  			MainError: err,
   670  		}
   671  	case err != nil:
   672  		event.Error(ctx, "initial workspace load failed", err)
   673  		extractedDiags, _ := s.extractGoCommandErrors(ctx, err.Error())
   674  		criticalErr = &source.CriticalError{
   675  			MainError: err,
   676  			DiagList:  append(modDiagnostics, extractedDiags...),
   677  		}
   678  	case len(modDiagnostics) == 1:
   679  		criticalErr = &source.CriticalError{
   680  			MainError: fmt.Errorf(modDiagnostics[0].Message),
   681  			DiagList:  modDiagnostics,
   682  		}
   683  	case len(modDiagnostics) > 1:
   684  		criticalErr = &source.CriticalError{
   685  			MainError: fmt.Errorf("error loading module names"),
   686  			DiagList:  modDiagnostics,
   687  		}
   688  	}
   689  
   690  	// Lock the snapshot when setting the initialized error.
   691  	s.mu.Lock()
   692  	defer s.mu.Unlock()
   693  	s.initializedErr = criticalErr
   694  }
   695  
   696  // invalidateContent invalidates the content of a Go file,
   697  // including any position and type information that depends on it.
   698  //
   699  // invalidateContent returns a non-nil snapshot for the new content, along with
   700  // a callback which the caller must invoke to release that snapshot.
   701  func (v *View) invalidateContent(ctx context.Context, changes map[span.URI]*fileChange, forceReloadMetadata bool) (*snapshot, func()) {
   702  	// Detach the context so that content invalidation cannot be canceled.
   703  	ctx = xcontext.Detach(ctx)
   704  
   705  	// This should be the only time we hold the view's snapshot lock for any period of time.
   706  	v.snapshotMu.Lock()
   707  	defer v.snapshotMu.Unlock()
   708  
   709  	if v.snapshot == nil {
   710  		panic("invalidateContent called after shutdown")
   711  	}
   712  
   713  	// Cancel all still-running previous requests, since they would be
   714  	// operating on stale data.
   715  	v.snapshot.cancel()
   716  
   717  	// Do not clone a snapshot until its view has finished initializing.
   718  	v.snapshot.AwaitInitialized(ctx)
   719  
   720  	oldSnapshot := v.snapshot
   721  
   722  	v.snapshot = oldSnapshot.clone(ctx, v.baseCtx, changes, forceReloadMetadata)
   723  	go oldSnapshot.generation.Destroy("View.invalidateContent")
   724  
   725  	return v.snapshot, v.snapshot.generation.Acquire()
   726  }
   727  
   728  func (s *Session) getWorkspaceInformation(ctx context.Context, folder span.URI, options *source.Options) (*workspaceInformation, error) {
   729  	if err := checkPathCase(folder.Filename()); err != nil {
   730  		return nil, errors.Errorf("invalid workspace folder path: %w; check that the casing of the configured workspace folder path agrees with the casing reported by the operating system", err)
   731  	}
   732  	var err error
   733  	inv := gocommand.Invocation{
   734  		WorkingDir: folder.Filename(),
   735  		Env:        options.EnvSlice(),
   736  	}
   737  	goversion, err := gocommand.GoVersion(ctx, inv, s.gocmdRunner)
   738  	if err != nil {
   739  		return nil, err
   740  	}
   741  
   742  	go111module := os.Getenv("GO111MODULE")
   743  	if v, ok := options.Env["GO111MODULE"]; ok {
   744  		go111module = v
   745  	}
   746  	// Make sure to get the `go env` before continuing with initialization.
   747  	envVars, env, err := s.getGoEnv(ctx, folder.Filename(), goversion, go111module, options.EnvSlice())
   748  	if err != nil {
   749  		return nil, err
   750  	}
   751  	// If using 1.16, change the default back to auto. The primary effect of
   752  	// GO111MODULE=on is to break GOPATH, which we aren't too interested in.
   753  	if goversion >= 16 && go111module == "" {
   754  		go111module = "auto"
   755  	}
   756  	// The value of GOPACKAGESDRIVER is not returned through the go command.
   757  	gopackagesdriver := os.Getenv("GOPACKAGESDRIVER")
   758  	for _, s := range env {
   759  		split := strings.SplitN(s, "=", 2)
   760  		if split[0] == "GOPACKAGESDRIVER" {
   761  			gopackagesdriver = split[1]
   762  		}
   763  	}
   764  
   765  	// A user may also have a gopackagesdriver binary on their machine, which
   766  	// works the same way as setting GOPACKAGESDRIVER.
   767  	tool, _ := exec.LookPath("gopackagesdriver")
   768  	hasGopackagesDriver := gopackagesdriver != "off" && (gopackagesdriver != "" || tool != "")
   769  
   770  	return &workspaceInformation{
   771  		hasGopackagesDriver:  hasGopackagesDriver,
   772  		effectiveGo111Module: go111module,
   773  		userGo111Module:      go111moduleForVersion(go111module, goversion),
   774  		goversion:            goversion,
   775  		environmentVariables: envVars,
   776  		goEnv:                env,
   777  	}, nil
   778  }
   779  
   780  func go111moduleForVersion(go111module string, goversion int) go111module {
   781  	// Off by default until Go 1.12.
   782  	if go111module == "off" || (goversion < 12 && go111module == "") {
   783  		return off
   784  	}
   785  	// On by default as of Go 1.16.
   786  	if go111module == "on" || (goversion >= 16 && go111module == "") {
   787  		return on
   788  	}
   789  	return auto
   790  }
   791  
   792  // findWorkspaceRoot searches for the best workspace root according to the
   793  // following heuristics:
   794  //   - First, look for a parent directory containing a gopls.mod file
   795  //     (experimental only).
   796  //   - Then, a parent directory containing a go.mod file.
   797  //   - Then, a child directory containing a go.mod file, if there is exactly
   798  //     one (non-experimental only).
   799  // Otherwise, it returns folder.
   800  // TODO (rFindley): move this to workspace.go
   801  // TODO (rFindley): simplify this once workspace modules are enabled by default.
   802  func findWorkspaceRoot(ctx context.Context, folder span.URI, fs source.FileSource, excludePath func(string) bool, experimental bool) (span.URI, error) {
   803  	patterns := []string{"go.work", "go.mod"}
   804  	if experimental {
   805  		patterns = []string{"go.work", "gopls.mod", "go.mod"}
   806  	}
   807  	for _, basename := range patterns {
   808  		dir, err := findRootPattern(ctx, folder, basename, fs)
   809  		if err != nil {
   810  			return "", errors.Errorf("finding %s: %w", basename, err)
   811  		}
   812  		if dir != "" {
   813  			return dir, nil
   814  		}
   815  	}
   816  
   817  	// The experimental workspace can handle nested modules at this point...
   818  	if experimental {
   819  		return folder, nil
   820  	}
   821  
   822  	// ...else we should check if there's exactly one nested module.
   823  	all, err := findModules(folder, excludePath, 2)
   824  	if err == errExhausted {
   825  		// Fall-back behavior: if we don't find any modules after searching 10000
   826  		// files, assume there are none.
   827  		event.Log(ctx, fmt.Sprintf("stopped searching for modules after %d files", fileLimit))
   828  		return folder, nil
   829  	}
   830  	if err != nil {
   831  		return "", err
   832  	}
   833  	if len(all) == 1 {
   834  		// range to access first element.
   835  		for uri := range all {
   836  			return dirURI(uri), nil
   837  		}
   838  	}
   839  	return folder, nil
   840  }
   841  
   842  func findRootPattern(ctx context.Context, folder span.URI, basename string, fs source.FileSource) (span.URI, error) {
   843  	dir := folder.Filename()
   844  	for dir != "" {
   845  		target := filepath.Join(dir, basename)
   846  		exists, err := fileExists(ctx, span.URIFromPath(target), fs)
   847  		if err != nil {
   848  			return "", err
   849  		}
   850  		if exists {
   851  			return span.URIFromPath(dir), nil
   852  		}
   853  		// Trailing separators must be trimmed, otherwise filepath.Split is a noop.
   854  		next, _ := filepath.Split(strings.TrimRight(dir, string(filepath.Separator)))
   855  		if next == dir {
   856  			break
   857  		}
   858  		dir = next
   859  	}
   860  	return "", nil
   861  }
   862  
   863  // OS-specific path case check, for case-insensitive filesystems.
   864  var checkPathCase = defaultCheckPathCase
   865  
   866  func defaultCheckPathCase(path string) error {
   867  	return nil
   868  }
   869  
   870  func validBuildConfiguration(folder span.URI, ws *workspaceInformation, modFiles map[span.URI]struct{}) bool {
   871  	// Since we only really understand the `go` command, if the user has a
   872  	// different GOPACKAGESDRIVER, assume that their configuration is valid.
   873  	if ws.hasGopackagesDriver {
   874  		return true
   875  	}
   876  	// Check if the user is working within a module or if we have found
   877  	// multiple modules in the workspace.
   878  	if len(modFiles) > 0 {
   879  		return true
   880  	}
   881  	// The user may have a multiple directories in their GOPATH.
   882  	// Check if the workspace is within any of them.
   883  	for _, gp := range filepath.SplitList(ws.gopath) {
   884  		if source.InDir(filepath.Join(gp, "src"), folder.Filename()) {
   885  			return true
   886  		}
   887  	}
   888  	return false
   889  }
   890  
   891  // getGoEnv gets the view's various GO* values.
   892  func (s *Session) getGoEnv(ctx context.Context, folder string, goversion int, go111module string, configEnv []string) (environmentVariables, map[string]string, error) {
   893  	envVars := environmentVariables{}
   894  	vars := map[string]*string{
   895  		"GOCACHE":     &envVars.gocache,
   896  		"GOPATH":      &envVars.gopath,
   897  		"GOROOT":      &envVars.goroot,
   898  		"GOPRIVATE":   &envVars.goprivate,
   899  		"GOMODCACHE":  &envVars.gomodcache,
   900  		"GO111MODULE": &envVars.go111module,
   901  	}
   902  
   903  	// We can save ~200 ms by requesting only the variables we care about.
   904  	args := append([]string{"-json"}, imports.RequiredGoEnvVars...)
   905  	for k := range vars {
   906  		args = append(args, k)
   907  	}
   908  	args = append(args, "GOWORK")
   909  
   910  	inv := gocommand.Invocation{
   911  		Verb:       "env",
   912  		Args:       args,
   913  		Env:        configEnv,
   914  		WorkingDir: folder,
   915  	}
   916  	// Don't go through runGoCommand, as we don't need a temporary -modfile to
   917  	// run `go env`.
   918  	stdout, err := s.gocmdRunner.Run(ctx, inv)
   919  	if err != nil {
   920  		return environmentVariables{}, nil, err
   921  	}
   922  	env := make(map[string]string)
   923  	if err := json.Unmarshal(stdout.Bytes(), &env); err != nil {
   924  		return environmentVariables{}, nil, err
   925  	}
   926  
   927  	for key, ptr := range vars {
   928  		*ptr = env[key]
   929  	}
   930  
   931  	// Old versions of Go don't have GOMODCACHE, so emulate it.
   932  	if envVars.gomodcache == "" && envVars.gopath != "" {
   933  		envVars.gomodcache = filepath.Join(filepath.SplitList(envVars.gopath)[0], "pkg/mod")
   934  	}
   935  	// GO111MODULE does not appear in `go env` output until Go 1.13.
   936  	if goversion < 13 {
   937  		envVars.go111module = go111module
   938  	}
   939  	return envVars, env, err
   940  }
   941  
   942  func (v *View) IsGoPrivatePath(target string) bool {
   943  	return globsMatchPath(v.goprivate, target)
   944  }
   945  
   946  func (v *View) ModuleUpgrades() map[string]string {
   947  	v.mu.Lock()
   948  	defer v.mu.Unlock()
   949  
   950  	upgrades := map[string]string{}
   951  	for mod, ver := range v.moduleUpgrades {
   952  		upgrades[mod] = ver
   953  	}
   954  	return upgrades
   955  }
   956  
   957  func (v *View) RegisterModuleUpgrades(upgrades map[string]string) {
   958  	v.mu.Lock()
   959  	defer v.mu.Unlock()
   960  
   961  	for mod, ver := range upgrades {
   962  		v.moduleUpgrades[mod] = ver
   963  	}
   964  }
   965  
   966  // Copied from
   967  // https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/str/path.go;l=58;drc=2910c5b4a01a573ebc97744890a07c1a3122c67a
   968  func globsMatchPath(globs, target string) bool {
   969  	for globs != "" {
   970  		// Extract next non-empty glob in comma-separated list.
   971  		var glob string
   972  		if i := strings.Index(globs, ","); i >= 0 {
   973  			glob, globs = globs[:i], globs[i+1:]
   974  		} else {
   975  			glob, globs = globs, ""
   976  		}
   977  		if glob == "" {
   978  			continue
   979  		}
   980  
   981  		// A glob with N+1 path elements (N slashes) needs to be matched
   982  		// against the first N+1 path elements of target,
   983  		// which end just before the N+1'th slash.
   984  		n := strings.Count(glob, "/")
   985  		prefix := target
   986  		// Walk target, counting slashes, truncating at the N+1'th slash.
   987  		for i := 0; i < len(target); i++ {
   988  			if target[i] == '/' {
   989  				if n == 0 {
   990  					prefix = target[:i]
   991  					break
   992  				}
   993  				n--
   994  			}
   995  		}
   996  		if n > 0 {
   997  			// Not enough prefix elements.
   998  			continue
   999  		}
  1000  		matched, _ := path.Match(glob, prefix)
  1001  		if matched {
  1002  			return true
  1003  		}
  1004  	}
  1005  	return false
  1006  }
  1007  
  1008  var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
  1009  
  1010  // TODO(rstambler): Consolidate modURI and modContent back into a FileHandle
  1011  // after we have a version of the workspace go.mod file on disk. Getting a
  1012  // FileHandle from the cache for temporary files is problematic, since we
  1013  // cannot delete it.
  1014  func (s *snapshot) vendorEnabled(ctx context.Context, modURI span.URI, modContent []byte) (bool, error) {
  1015  	if s.workspaceMode()&moduleMode == 0 {
  1016  		return false, nil
  1017  	}
  1018  	matches := modFlagRegexp.FindStringSubmatch(s.view.goEnv["GOFLAGS"])
  1019  	var modFlag string
  1020  	if len(matches) != 0 {
  1021  		modFlag = matches[1]
  1022  	}
  1023  	if modFlag != "" {
  1024  		// Don't override an explicit '-mod=vendor' argument.
  1025  		// We do want to override '-mod=readonly': it would break various module code lenses,
  1026  		// and on 1.16 we know -modfile is available, so we won't mess with go.mod anyway.
  1027  		return modFlag == "vendor", nil
  1028  	}
  1029  
  1030  	modFile, err := modfile.Parse(modURI.Filename(), modContent, nil)
  1031  	if err != nil {
  1032  		return false, err
  1033  	}
  1034  	if fi, err := os.Stat(filepath.Join(s.view.rootURI.Filename(), "vendor")); err != nil || !fi.IsDir() {
  1035  		return false, nil
  1036  	}
  1037  	vendorEnabled := modFile.Go != nil && modFile.Go.Version != "" && semver.Compare("v"+modFile.Go.Version, "v1.14") >= 0
  1038  	return vendorEnabled, nil
  1039  }
  1040  
  1041  func (v *View) allFilesExcluded(pkg *packages.Package) bool {
  1042  	opts := v.Options()
  1043  	folder := filepath.ToSlash(v.folder.Filename())
  1044  	for _, f := range pkg.GoFiles {
  1045  		f = filepath.ToSlash(f)
  1046  		if !strings.HasPrefix(f, folder) {
  1047  			return false
  1048  		}
  1049  		if !pathExcludedByFilter(strings.TrimPrefix(f, folder), v.rootURI.Filename(), v.gomodcache, opts) {
  1050  			return false
  1051  		}
  1052  	}
  1053  	return true
  1054  }
  1055  
  1056  func pathExcludedByFilterFunc(root, gomodcache string, opts *source.Options) func(string) bool {
  1057  	return func(path string) bool {
  1058  		return pathExcludedByFilter(path, root, gomodcache, opts)
  1059  	}
  1060  }
  1061  
  1062  // pathExcludedByFilter reports whether the path (relative to the workspace
  1063  // folder) should be excluded by the configured directory filters.
  1064  //
  1065  // TODO(rfindley): passing root and gomodcache here makes it confusing whether
  1066  // path should be absolute or relative, and has already caused at least one
  1067  // bug.
  1068  func pathExcludedByFilter(path, root, gomodcache string, opts *source.Options) bool {
  1069  	path = strings.TrimPrefix(filepath.ToSlash(path), "/")
  1070  	gomodcache = strings.TrimPrefix(filepath.ToSlash(strings.TrimPrefix(gomodcache, root)), "/")
  1071  	filters := opts.DirectoryFilters
  1072  	if gomodcache != "" {
  1073  		filters = append(filters, "-"+gomodcache)
  1074  	}
  1075  	return source.FiltersDisallow(path, filters)
  1076  }