golang.org/x/tools/gopls@v0.15.3/internal/cache/session.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  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"sort"
    14  	"strconv"
    15  	"strings"
    16  	"sync"
    17  	"sync/atomic"
    18  	"time"
    19  
    20  	"golang.org/x/tools/gopls/internal/cache/metadata"
    21  	"golang.org/x/tools/gopls/internal/cache/typerefs"
    22  	"golang.org/x/tools/gopls/internal/file"
    23  	"golang.org/x/tools/gopls/internal/protocol"
    24  	"golang.org/x/tools/gopls/internal/util/bug"
    25  	"golang.org/x/tools/gopls/internal/util/persistent"
    26  	"golang.org/x/tools/gopls/internal/util/slices"
    27  	"golang.org/x/tools/gopls/internal/vulncheck"
    28  	"golang.org/x/tools/internal/event"
    29  	"golang.org/x/tools/internal/gocommand"
    30  	"golang.org/x/tools/internal/imports"
    31  	"golang.org/x/tools/internal/memoize"
    32  	"golang.org/x/tools/internal/xcontext"
    33  )
    34  
    35  // NewSession creates a new gopls session with the given cache.
    36  func NewSession(ctx context.Context, c *Cache) *Session {
    37  	index := atomic.AddInt64(&sessionIndex, 1)
    38  	s := &Session{
    39  		id:          strconv.FormatInt(index, 10),
    40  		cache:       c,
    41  		gocmdRunner: &gocommand.Runner{},
    42  		overlayFS:   newOverlayFS(c),
    43  		parseCache:  newParseCache(1 * time.Minute), // keep recently parsed files for a minute, to optimize typing CPU
    44  		viewMap:     make(map[protocol.DocumentURI]*View),
    45  	}
    46  	event.Log(ctx, "New session", KeyCreateSession.Of(s))
    47  	return s
    48  }
    49  
    50  // A Session holds the state (views, file contents, parse cache,
    51  // memoized computations) of a gopls server process.
    52  //
    53  // It implements the file.Source interface.
    54  type Session struct {
    55  	// Unique identifier for this session.
    56  	id string
    57  
    58  	// Immutable attributes shared across views.
    59  	cache       *Cache            // shared cache
    60  	gocmdRunner *gocommand.Runner // limits go command concurrency
    61  
    62  	viewMu  sync.Mutex
    63  	views   []*View
    64  	viewMap map[protocol.DocumentURI]*View // file->best view; nil after shutdown
    65  
    66  	// snapshots is a counting semaphore that records the number
    67  	// of unreleased snapshots associated with this session.
    68  	// Shutdown waits for it to fall to zero.
    69  	snapshotWG sync.WaitGroup
    70  
    71  	parseCache *parseCache
    72  
    73  	*overlayFS
    74  }
    75  
    76  // ID returns the unique identifier for this session on this server.
    77  func (s *Session) ID() string     { return s.id }
    78  func (s *Session) String() string { return s.id }
    79  
    80  // GoCommandRunner returns the gocommand Runner for this session.
    81  func (s *Session) GoCommandRunner() *gocommand.Runner {
    82  	return s.gocmdRunner
    83  }
    84  
    85  // Shutdown the session and all views it has created.
    86  func (s *Session) Shutdown(ctx context.Context) {
    87  	var views []*View
    88  	s.viewMu.Lock()
    89  	views = append(views, s.views...)
    90  	s.views = nil
    91  	s.viewMap = nil
    92  	s.viewMu.Unlock()
    93  	for _, view := range views {
    94  		view.shutdown()
    95  	}
    96  	s.parseCache.stop()
    97  	s.snapshotWG.Wait() // wait for all work on associated snapshots to finish
    98  	event.Log(ctx, "Shutdown session", KeyShutdownSession.Of(s))
    99  }
   100  
   101  // Cache returns the cache that created this session, for debugging only.
   102  func (s *Session) Cache() *Cache {
   103  	return s.cache
   104  }
   105  
   106  // TODO(rfindley): is the logic surrounding this error actually necessary?
   107  var ErrViewExists = errors.New("view already exists for session")
   108  
   109  // NewView creates a new View, returning it and its first snapshot. If a
   110  // non-empty tempWorkspace directory is provided, the View will record a copy
   111  // of its gopls workspace module in that directory, so that client tooling
   112  // can execute in the same main module.  On success it also returns a release
   113  // function that must be called when the Snapshot is no longer needed.
   114  func (s *Session) NewView(ctx context.Context, folder *Folder) (*View, *Snapshot, func(), error) {
   115  	s.viewMu.Lock()
   116  	defer s.viewMu.Unlock()
   117  
   118  	// Querying the file system to check whether
   119  	// two folders denote the same existing directory.
   120  	if inode1, err := os.Stat(filepath.FromSlash(folder.Dir.Path())); err == nil {
   121  		for _, view := range s.views {
   122  			inode2, err := os.Stat(filepath.FromSlash(view.folder.Dir.Path()))
   123  			if err == nil && os.SameFile(inode1, inode2) {
   124  				return nil, nil, nil, ErrViewExists
   125  			}
   126  		}
   127  	}
   128  
   129  	def, err := defineView(ctx, s, folder, nil)
   130  	if err != nil {
   131  		return nil, nil, nil, err
   132  	}
   133  	view, snapshot, release := s.createView(ctx, def)
   134  	s.views = append(s.views, view)
   135  	// we always need to drop the view map
   136  	s.viewMap = make(map[protocol.DocumentURI]*View)
   137  	return view, snapshot, release, nil
   138  }
   139  
   140  // createView creates a new view, with an initial snapshot that retains the
   141  // supplied context, detached from events and cancelation.
   142  //
   143  // The caller is responsible for calling the release function once.
   144  func (s *Session) createView(ctx context.Context, def *viewDefinition) (*View, *Snapshot, func()) {
   145  	index := atomic.AddInt64(&viewIndex, 1)
   146  
   147  	// We want a true background context and not a detached context here
   148  	// the spans need to be unrelated and no tag values should pollute it.
   149  	baseCtx := event.Detach(xcontext.Detach(ctx))
   150  	backgroundCtx, cancel := context.WithCancel(baseCtx)
   151  
   152  	// Compute a skip function to use for module cache scanning.
   153  	//
   154  	// Note that unlike other filtering operations, we definitely don't want to
   155  	// exclude the gomodcache here, even if it is contained in the workspace
   156  	// folder.
   157  	//
   158  	// TODO(rfindley): consolidate with relPathExcludedByFilter(Func), Filterer,
   159  	// View.filterFunc.
   160  	var skipPath func(string) bool
   161  	{
   162  		// Compute a prefix match, respecting segment boundaries, by ensuring
   163  		// the pattern (dir) has a trailing slash.
   164  		dirPrefix := strings.TrimSuffix(string(def.folder.Dir), "/") + "/"
   165  		filterer := NewFilterer(def.folder.Options.DirectoryFilters)
   166  		skipPath = func(dir string) bool {
   167  			uri := strings.TrimSuffix(string(protocol.URIFromPath(dir)), "/")
   168  			// Note that the logic below doesn't handle the case where uri ==
   169  			// v.folder.Dir, because there is no point in excluding the entire
   170  			// workspace folder!
   171  			if rel := strings.TrimPrefix(uri, dirPrefix); rel != uri {
   172  				return filterer.Disallow(rel)
   173  			}
   174  			return false
   175  		}
   176  	}
   177  
   178  	var ignoreFilter *ignoreFilter
   179  	{
   180  		var dirs []string
   181  		if len(def.workspaceModFiles) == 0 {
   182  			for _, entry := range filepath.SplitList(def.folder.Env.GOPATH) {
   183  				dirs = append(dirs, filepath.Join(entry, "src"))
   184  			}
   185  		} else {
   186  			dirs = append(dirs, def.folder.Env.GOMODCACHE)
   187  			for m := range def.workspaceModFiles {
   188  				dirs = append(dirs, filepath.Dir(m.Path()))
   189  			}
   190  		}
   191  		ignoreFilter = newIgnoreFilter(dirs)
   192  	}
   193  
   194  	var pe *imports.ProcessEnv
   195  	{
   196  		env := make(map[string]string)
   197  		envSlice := slices.Concat(os.Environ(), def.folder.Options.EnvSlice(), []string{"GO111MODULE=" + def.adjustedGO111MODULE()})
   198  		for _, kv := range envSlice {
   199  			if k, v, ok := strings.Cut(kv, "="); ok {
   200  				env[k] = v
   201  			}
   202  		}
   203  		pe = &imports.ProcessEnv{
   204  			GocmdRunner: s.gocmdRunner,
   205  			BuildFlags:  slices.Clone(def.folder.Options.BuildFlags),
   206  			// TODO(rfindley): an old comment said "processEnv operations should not mutate the modfile"
   207  			// But shouldn't we honor the default behavior of mod vendoring?
   208  			ModFlag:        "readonly",
   209  			SkipPathInScan: skipPath,
   210  			Env:            env,
   211  			WorkingDir:     def.root.Path(),
   212  			ModCache:       s.cache.modCache.dirCache(def.folder.Env.GOMODCACHE),
   213  		}
   214  		if def.folder.Options.VerboseOutput {
   215  			pe.Logf = func(format string, args ...interface{}) {
   216  				event.Log(ctx, fmt.Sprintf(format, args...))
   217  			}
   218  		}
   219  	}
   220  
   221  	v := &View{
   222  		id:                   strconv.FormatInt(index, 10),
   223  		gocmdRunner:          s.gocmdRunner,
   224  		initialWorkspaceLoad: make(chan struct{}),
   225  		initializationSema:   make(chan struct{}, 1),
   226  		baseCtx:              baseCtx,
   227  		parseCache:           s.parseCache,
   228  		ignoreFilter:         ignoreFilter,
   229  		fs:                   s.overlayFS,
   230  		viewDefinition:       def,
   231  		importsState:         newImportsState(backgroundCtx, s.cache.modCache, pe),
   232  	}
   233  
   234  	s.snapshotWG.Add(1)
   235  	v.snapshot = &Snapshot{
   236  		view:             v,
   237  		backgroundCtx:    backgroundCtx,
   238  		cancel:           cancel,
   239  		store:            s.cache.store,
   240  		refcount:         1, // Snapshots are born referenced.
   241  		done:             s.snapshotWG.Done,
   242  		packages:         new(persistent.Map[PackageID, *packageHandle]),
   243  		meta:             new(metadata.Graph),
   244  		files:            newFileMap(),
   245  		activePackages:   new(persistent.Map[PackageID, *Package]),
   246  		symbolizeHandles: new(persistent.Map[protocol.DocumentURI, *memoize.Promise]),
   247  		shouldLoad:       new(persistent.Map[PackageID, []PackagePath]),
   248  		unloadableFiles:  new(persistent.Set[protocol.DocumentURI]),
   249  		parseModHandles:  new(persistent.Map[protocol.DocumentURI, *memoize.Promise]),
   250  		parseWorkHandles: new(persistent.Map[protocol.DocumentURI, *memoize.Promise]),
   251  		modTidyHandles:   new(persistent.Map[protocol.DocumentURI, *memoize.Promise]),
   252  		modVulnHandles:   new(persistent.Map[protocol.DocumentURI, *memoize.Promise]),
   253  		modWhyHandles:    new(persistent.Map[protocol.DocumentURI, *memoize.Promise]),
   254  		pkgIndex:         typerefs.NewPackageIndex(),
   255  		moduleUpgrades:   new(persistent.Map[protocol.DocumentURI, map[string]string]),
   256  		vulns:            new(persistent.Map[protocol.DocumentURI, *vulncheck.Result]),
   257  	}
   258  
   259  	// Snapshots must observe all open files, as there are some caching
   260  	// heuristics that change behavior depending on open files.
   261  	for _, o := range s.overlayFS.Overlays() {
   262  		_, _ = v.snapshot.ReadFile(ctx, o.URI())
   263  	}
   264  
   265  	// Record the environment of the newly created view in the log.
   266  	event.Log(ctx, viewEnv(v))
   267  
   268  	// Initialize the view without blocking.
   269  	initCtx, initCancel := context.WithCancel(xcontext.Detach(ctx))
   270  	v.cancelInitialWorkspaceLoad = initCancel
   271  	snapshot := v.snapshot
   272  
   273  	// Pass a second reference to the background goroutine.
   274  	bgRelease := snapshot.Acquire()
   275  	go func() {
   276  		defer bgRelease()
   277  		snapshot.initialize(initCtx, true)
   278  	}()
   279  
   280  	// Return a third reference to the caller.
   281  	return v, snapshot, snapshot.Acquire()
   282  }
   283  
   284  // RemoveView removes from the session the view rooted at the specified directory.
   285  // It reports whether a view of that directory was removed.
   286  func (s *Session) RemoveView(dir protocol.DocumentURI) bool {
   287  	s.viewMu.Lock()
   288  	defer s.viewMu.Unlock()
   289  	for _, view := range s.views {
   290  		if view.folder.Dir == dir {
   291  			i := s.dropView(view)
   292  			if i == -1 {
   293  				return false // can't happen
   294  			}
   295  			// delete this view... we don't care about order but we do want to make
   296  			// sure we can garbage collect the view
   297  			s.views = removeElement(s.views, i)
   298  			return true
   299  		}
   300  	}
   301  	return false
   302  }
   303  
   304  // View returns the view with a matching id, if present.
   305  func (s *Session) View(id string) (*View, error) {
   306  	s.viewMu.Lock()
   307  	defer s.viewMu.Unlock()
   308  	for _, view := range s.views {
   309  		if view.ID() == id {
   310  			return view, nil
   311  		}
   312  	}
   313  	return nil, fmt.Errorf("no view with ID %q", id)
   314  }
   315  
   316  // SnapshotOf returns a Snapshot corresponding to the given URI.
   317  //
   318  // In the case where the file can be  can be associated with a View by
   319  // bestViewForURI (based on directory information alone, without package
   320  // metadata), SnapshotOf returns the current Snapshot for that View. Otherwise,
   321  // it awaits loading package metadata and returns a Snapshot for the first View
   322  // containing a real (=not command-line-arguments) package for the file.
   323  //
   324  // If that also fails to find a View, SnapshotOf returns a Snapshot for the
   325  // first view in s.views that is not shut down (i.e. s.views[0] unless we lose
   326  // a race), for determinism in tests and so that we tend to aggregate the
   327  // resulting command-line-arguments packages into a single view.
   328  //
   329  // SnapshotOf returns an error if a failure occurs along the way (most likely due
   330  // to context cancellation), or if there are no Views in the Session.
   331  //
   332  // On success, the caller must call the returned function to release the snapshot.
   333  func (s *Session) SnapshotOf(ctx context.Context, uri protocol.DocumentURI) (*Snapshot, func(), error) {
   334  	// Fast path: if the uri has a static association with a view, return it.
   335  	s.viewMu.Lock()
   336  	v, err := s.viewOfLocked(ctx, uri)
   337  	s.viewMu.Unlock()
   338  
   339  	if err != nil {
   340  		return nil, nil, err
   341  	}
   342  
   343  	if v != nil {
   344  		snapshot, release, err := v.Snapshot()
   345  		if err == nil {
   346  			return snapshot, release, nil
   347  		}
   348  		// View is shut down. Forget this association.
   349  		s.viewMu.Lock()
   350  		if s.viewMap[uri] == v {
   351  			delete(s.viewMap, uri)
   352  		}
   353  		s.viewMu.Unlock()
   354  	}
   355  
   356  	// Fall-back: none of the views could be associated with uri based on
   357  	// directory information alone.
   358  	//
   359  	// Don't memoize the view association in viewMap, as it is not static: Views
   360  	// may change as metadata changes.
   361  	//
   362  	// TODO(rfindley): we could perhaps optimize this case by peeking at existing
   363  	// metadata before awaiting the load (after all, a load only adds metadata).
   364  	// But that seems potentially tricky, when in the common case no loading
   365  	// should be required.
   366  	views := s.Views()
   367  	for _, v := range views {
   368  		snapshot, release, err := v.Snapshot()
   369  		if err != nil {
   370  			continue // view was shut down
   371  		}
   372  		_ = snapshot.awaitLoaded(ctx) // ignore error
   373  		g := snapshot.MetadataGraph()
   374  		// We don't check the error from awaitLoaded, because a load failure (that
   375  		// doesn't result from context cancelation) should not prevent us from
   376  		// continuing to search for the best view.
   377  		if ctx.Err() != nil {
   378  			release()
   379  			return nil, nil, ctx.Err()
   380  		}
   381  		// Special handling for the builtin file, since it doesn't have packages.
   382  		if snapshot.IsBuiltin(uri) {
   383  			return snapshot, release, nil
   384  		}
   385  		// Only match this view if it loaded a real package for the file.
   386  		//
   387  		// Any view can load a command-line-arguments package; aggregate those into
   388  		// views[0] below.
   389  		for _, id := range g.IDs[uri] {
   390  			if !metadata.IsCommandLineArguments(id) || g.Packages[id].Standalone {
   391  				return snapshot, release, nil
   392  			}
   393  		}
   394  		release()
   395  	}
   396  
   397  	for _, v := range views {
   398  		snapshot, release, err := v.Snapshot()
   399  		if err == nil {
   400  			return snapshot, release, nil // first valid snapshot
   401  		}
   402  	}
   403  	return nil, nil, errNoViews
   404  }
   405  
   406  // errNoViews is sought by orphaned file diagnostics, to detect the case where
   407  // we have no view containing a file.
   408  var errNoViews = errors.New("no views")
   409  
   410  // viewOfLocked wraps bestViewForURI, memoizing its result.
   411  //
   412  // Precondition: caller holds s.viewMu lock.
   413  //
   414  // May return (nil, nil).
   415  func (s *Session) viewOfLocked(ctx context.Context, uri protocol.DocumentURI) (*View, error) {
   416  	v, hit := s.viewMap[uri]
   417  	if !hit {
   418  		// Cache miss: compute (and memoize) the best view.
   419  		fh, err := s.ReadFile(ctx, uri)
   420  		if err != nil {
   421  			return nil, err
   422  		}
   423  		v, err = bestView(ctx, s, fh, s.views)
   424  		if err != nil {
   425  			return nil, err
   426  		}
   427  		if s.viewMap == nil {
   428  			return nil, errors.New("session is shut down")
   429  		}
   430  		s.viewMap[uri] = v
   431  	}
   432  	return v, nil
   433  }
   434  
   435  func (s *Session) Views() []*View {
   436  	s.viewMu.Lock()
   437  	defer s.viewMu.Unlock()
   438  	result := make([]*View, len(s.views))
   439  	copy(result, s.views)
   440  	return result
   441  }
   442  
   443  // selectViewDefs constructs the best set of views covering the provided workspace
   444  // folders and open files.
   445  //
   446  // This implements the zero-config algorithm of golang/go#57979.
   447  func selectViewDefs(ctx context.Context, fs file.Source, folders []*Folder, openFiles []protocol.DocumentURI) ([]*viewDefinition, error) {
   448  	var defs []*viewDefinition
   449  
   450  	// First, compute a default view for each workspace folder.
   451  	// TODO(golang/go#57979): technically, this is path dependent, since
   452  	// DidChangeWorkspaceFolders could introduce a path-dependent ordering on
   453  	// folders. We should keep folders sorted, or sort them here.
   454  	for _, folder := range folders {
   455  		def, err := defineView(ctx, fs, folder, nil)
   456  		if err != nil {
   457  			return nil, err
   458  		}
   459  		defs = append(defs, def)
   460  	}
   461  
   462  	// Next, ensure that the set of views covers all open files contained in a
   463  	// workspace folder.
   464  	//
   465  	// We only do this for files contained in a workspace folder, because other
   466  	// open files are most likely the result of jumping to a definition from a
   467  	// workspace file; we don't want to create additional views in those cases:
   468  	// they should be resolved after initialization.
   469  
   470  	folderForFile := func(uri protocol.DocumentURI) *Folder {
   471  		var longest *Folder
   472  		for _, folder := range folders {
   473  			// Check that this is a better match than longest, but not through a
   474  			// vendor directory. Count occurrences of "/vendor/" as a quick check
   475  			// that the vendor directory is between the folder and the file. Note the
   476  			// addition of a trailing "/" to handle the odd case where the folder is named
   477  			// vendor (which I hope is exceedingly rare in any case).
   478  			//
   479  			// Vendored packages are, by definition, part of an existing view.
   480  			if (longest == nil || len(folder.Dir) > len(longest.Dir)) &&
   481  				folder.Dir.Encloses(uri) &&
   482  				strings.Count(string(uri), "/vendor/") == strings.Count(string(folder.Dir)+"/", "/vendor/") {
   483  
   484  				longest = folder
   485  			}
   486  		}
   487  		return longest
   488  	}
   489  
   490  checkFiles:
   491  	for _, uri := range openFiles {
   492  		folder := folderForFile(uri)
   493  		if folder == nil || !folder.Options.ZeroConfig {
   494  			continue // only guess views for open files
   495  		}
   496  		fh, err := fs.ReadFile(ctx, uri)
   497  		if err != nil {
   498  			return nil, err
   499  		}
   500  		def, err := bestView(ctx, fs, fh, defs)
   501  		if err != nil {
   502  			// We should never call selectViewDefs with a cancellable context, so
   503  			// this should never fail.
   504  			return nil, bug.Errorf("failed to find best view for open file: %v", err)
   505  		}
   506  		if def != nil {
   507  			continue // file covered by an existing view
   508  		}
   509  		def, err = defineView(ctx, fs, folder, fh)
   510  		if err != nil {
   511  			// We should never call selectViewDefs with a cancellable context, so
   512  			// this should never fail.
   513  			return nil, bug.Errorf("failed to define view for open file: %v", err)
   514  		}
   515  		// It need not strictly be the case that the best view for a file is
   516  		// distinct from other views, as the logic of getViewDefinition and
   517  		// bestViewForURI does not align perfectly. This is not necessarily a bug:
   518  		// there may be files for which we can't construct a valid view.
   519  		//
   520  		// Nevertheless, we should not create redundant views.
   521  		for _, alt := range defs {
   522  			if viewDefinitionsEqual(alt, def) {
   523  				continue checkFiles
   524  			}
   525  		}
   526  		defs = append(defs, def)
   527  	}
   528  
   529  	return defs, nil
   530  }
   531  
   532  // The viewDefiner interface allows the bestView algorithm to operate on both
   533  // Views and viewDefinitions.
   534  type viewDefiner interface{ definition() *viewDefinition }
   535  
   536  // BestViews returns the most relevant subset of views for a given uri.
   537  //
   538  // This may be used to filter diagnostics to the most relevant builds.
   539  func BestViews[V viewDefiner](ctx context.Context, fs file.Source, uri protocol.DocumentURI, views []V) ([]V, error) {
   540  	if len(views) == 0 {
   541  		return nil, nil // avoid the call to findRootPattern
   542  	}
   543  	dir := uri.Dir()
   544  	modURI, err := findRootPattern(ctx, dir, "go.mod", fs)
   545  	if err != nil {
   546  		return nil, err
   547  	}
   548  
   549  	// Prefer GoWork > GoMod > GOPATH > GoPackages > AdHoc.
   550  	var (
   551  		goPackagesViews []V // prefer longest
   552  		workViews       []V // prefer longest
   553  		modViews        []V // exact match
   554  		gopathViews     []V // prefer longest
   555  		adHocViews      []V // exact match
   556  	)
   557  
   558  	// pushView updates the views slice with the matching view v, using the
   559  	// heuristic that views with a longer root are preferable. Accordingly,
   560  	// pushView may be a no op if v's root is shorter than the roots in the views
   561  	// slice.
   562  	//
   563  	// Invariant: the length of all roots in views is the same.
   564  	pushView := func(views *[]V, v V) {
   565  		if len(*views) == 0 {
   566  			*views = []V{v}
   567  			return
   568  		}
   569  		better := func(l, r V) bool {
   570  			return len(l.definition().root) > len(r.definition().root)
   571  		}
   572  		existing := (*views)[0]
   573  		switch {
   574  		case better(existing, v):
   575  		case better(v, existing):
   576  			*views = []V{v}
   577  		default:
   578  			*views = append(*views, v)
   579  		}
   580  	}
   581  
   582  	for _, view := range views {
   583  		switch def := view.definition(); def.Type() {
   584  		case GoPackagesDriverView:
   585  			if def.root.Encloses(dir) {
   586  				pushView(&goPackagesViews, view)
   587  			}
   588  		case GoWorkView:
   589  			if _, ok := def.workspaceModFiles[modURI]; ok || uri == def.gowork {
   590  				pushView(&workViews, view)
   591  			}
   592  		case GoModView:
   593  			if _, ok := def.workspaceModFiles[modURI]; ok {
   594  				modViews = append(modViews, view)
   595  			}
   596  		case GOPATHView:
   597  			if def.root.Encloses(dir) {
   598  				pushView(&gopathViews, view)
   599  			}
   600  		case AdHocView:
   601  			if def.root == dir {
   602  				adHocViews = append(adHocViews, view)
   603  			}
   604  		}
   605  	}
   606  
   607  	// Now that we've collected matching views, choose the best match,
   608  	// considering ports.
   609  	//
   610  	// We only consider one type of view, since the matching view created by
   611  	// defineView should be of the best type.
   612  	var bestViews []V
   613  	switch {
   614  	case len(workViews) > 0:
   615  		bestViews = workViews
   616  	case len(modViews) > 0:
   617  		bestViews = modViews
   618  	case len(gopathViews) > 0:
   619  		bestViews = gopathViews
   620  	case len(goPackagesViews) > 0:
   621  		bestViews = goPackagesViews
   622  	case len(adHocViews) > 0:
   623  		bestViews = adHocViews
   624  	}
   625  
   626  	return bestViews, nil
   627  }
   628  
   629  // bestView returns the best View or viewDefinition that contains the
   630  // given file, or (nil, nil) if no matching view is found.
   631  //
   632  // bestView only returns an error in the event of context cancellation.
   633  //
   634  // Making this function generic is convenient so that we can avoid mapping view
   635  // definitions back to views inside Session.DidModifyFiles, where performance
   636  // matters. It is, however, not the cleanest application of generics.
   637  //
   638  // Note: keep this function in sync with defineView.
   639  func bestView[V viewDefiner](ctx context.Context, fs file.Source, fh file.Handle, views []V) (V, error) {
   640  	var zero V
   641  	bestViews, err := BestViews(ctx, fs, fh.URI(), views)
   642  	if err != nil || len(bestViews) == 0 {
   643  		return zero, err
   644  	}
   645  
   646  	content, err := fh.Content()
   647  	// Port matching doesn't apply to non-go files, or files that no longer exist.
   648  	// Note that the behavior here on non-existent files shouldn't matter much,
   649  	// since there will be a subsequent failure. But it is simpler to preserve
   650  	// the invariant that bestView only fails on context cancellation.
   651  	if fileKind(fh) != file.Go || err != nil {
   652  		return bestViews[0], nil
   653  	}
   654  
   655  	// Find the first view that matches constraints.
   656  	// Content trimming is nontrivial, so do this outside of the loop below.
   657  	path := fh.URI().Path()
   658  	content = trimContentForPortMatch(content)
   659  	for _, v := range bestViews {
   660  		def := v.definition()
   661  		viewPort := port{def.GOOS(), def.GOARCH()}
   662  		if viewPort.matches(path, content) {
   663  			return v, nil
   664  		}
   665  	}
   666  
   667  	return zero, nil // no view found
   668  }
   669  
   670  // updateViewLocked recreates the view with the given options.
   671  //
   672  // If the resulting error is non-nil, the view may or may not have already been
   673  // dropped from the session.
   674  func (s *Session) updateViewLocked(ctx context.Context, view *View, def *viewDefinition) (*View, error) {
   675  	i := s.dropView(view)
   676  	if i == -1 {
   677  		return nil, fmt.Errorf("view %q not found", view.id)
   678  	}
   679  
   680  	view, _, release := s.createView(ctx, def)
   681  	defer release()
   682  
   683  	// substitute the new view into the array where the old view was
   684  	s.views[i] = view
   685  	s.viewMap = make(map[protocol.DocumentURI]*View)
   686  	return view, nil
   687  }
   688  
   689  // removeElement removes the ith element from the slice replacing it with the last element.
   690  // TODO(adonovan): generics, someday.
   691  func removeElement(slice []*View, index int) []*View {
   692  	last := len(slice) - 1
   693  	slice[index] = slice[last]
   694  	slice[last] = nil // aid GC
   695  	return slice[:last]
   696  }
   697  
   698  // dropView removes v from the set of views for the receiver s and calls
   699  // v.shutdown, returning the index of v in s.views (if found), or -1 if v was
   700  // not found. s.viewMu must be held while calling this function.
   701  func (s *Session) dropView(v *View) int {
   702  	// we always need to drop the view map
   703  	s.viewMap = make(map[protocol.DocumentURI]*View)
   704  	for i := range s.views {
   705  		if v == s.views[i] {
   706  			// we found the view, drop it and return the index it was found at
   707  			s.views[i] = nil
   708  			v.shutdown()
   709  			return i
   710  		}
   711  	}
   712  	// TODO(rfindley): it looks wrong that we don't shutdown v in this codepath.
   713  	// We should never get here.
   714  	bug.Reportf("tried to drop nonexistent view %q", v.id)
   715  	return -1
   716  }
   717  
   718  // ResetView resets the best view for the given URI.
   719  func (s *Session) ResetView(ctx context.Context, uri protocol.DocumentURI) (*View, error) {
   720  	s.viewMu.Lock()
   721  	defer s.viewMu.Unlock()
   722  	v, err := s.viewOfLocked(ctx, uri)
   723  	if err != nil {
   724  		return nil, err
   725  	}
   726  	return s.updateViewLocked(ctx, v, v.viewDefinition)
   727  }
   728  
   729  // DidModifyFiles reports a file modification to the session. It returns
   730  // the new snapshots after the modifications have been applied, paired with
   731  // the affected file URIs for those snapshots.
   732  // On success, it returns a release function that
   733  // must be called when the snapshots are no longer needed.
   734  //
   735  // TODO(rfindley): what happens if this function fails? It must leave us in a
   736  // broken state, which we should surface to the user, probably as a request to
   737  // restart gopls.
   738  func (s *Session) DidModifyFiles(ctx context.Context, modifications []file.Modification) (map[*View][]protocol.DocumentURI, error) {
   739  	s.viewMu.Lock()
   740  	defer s.viewMu.Unlock()
   741  
   742  	// Update overlays.
   743  	//
   744  	// This is done while holding viewMu because the set of open files affects
   745  	// the set of views, and to prevent views from seeing updated file content
   746  	// before they have processed invalidations.
   747  	replaced, err := s.updateOverlays(ctx, modifications)
   748  	if err != nil {
   749  		return nil, err
   750  	}
   751  
   752  	// checkViews controls whether the set of views needs to be recomputed, for
   753  	// example because a go.mod file was created or deleted, or a go.work file
   754  	// changed on disk.
   755  	checkViews := false
   756  
   757  	changed := make(map[protocol.DocumentURI]file.Handle)
   758  	for _, c := range modifications {
   759  		fh := mustReadFile(ctx, s, c.URI)
   760  		changed[c.URI] = fh
   761  
   762  		// Any change to the set of open files causes views to be recomputed.
   763  		if c.Action == file.Open || c.Action == file.Close {
   764  			checkViews = true
   765  		}
   766  
   767  		// Any on-disk change to a go.work or go.mod file causes recomputing views.
   768  		//
   769  		// TODO(rfindley): go.work files need not be named "go.work" -- we need to
   770  		// check each view's source to handle the case of an explicit GOWORK value.
   771  		// Write a test that fails, and fix this.
   772  		if (isGoWork(c.URI) || isGoMod(c.URI)) && (c.Action == file.Save || c.OnDisk) {
   773  			checkViews = true
   774  		}
   775  
   776  		// Any change to the set of supported ports in a file may affect view
   777  		// selection. This is perhaps more subtle than it first seems: since the
   778  		// algorithm for selecting views considers open files in a deterministic
   779  		// order, a change in supported ports may cause a different port to be
   780  		// chosen, even if all open files still match an existing View!
   781  		//
   782  		// We endeavor to avoid that sort of path dependence, so must re-run the
   783  		// view selection algorithm whenever any input changes.
   784  		//
   785  		// However, extracting the build comment is nontrivial, so we don't want to
   786  		// pay this cost when e.g. processing a bunch of on-disk changes due to a
   787  		// branch change. Be careful to only do this if both files are open Go
   788  		// files.
   789  		if old, ok := replaced[c.URI]; ok && !checkViews && fileKind(fh) == file.Go {
   790  			if new, ok := fh.(*overlay); ok {
   791  				if buildComment(old.content) != buildComment(new.content) {
   792  					checkViews = true
   793  				}
   794  			}
   795  		}
   796  	}
   797  
   798  	if checkViews {
   799  		// Hack: collect folders from existing views.
   800  		// TODO(golang/go#57979): we really should track folders independent of
   801  		// Views, but since we always have a default View for each folder, this
   802  		// works for now.
   803  		var folders []*Folder // preserve folder order
   804  		seen := make(map[*Folder]unit)
   805  		for _, v := range s.views {
   806  			if _, ok := seen[v.folder]; ok {
   807  				continue
   808  			}
   809  			seen[v.folder] = unit{}
   810  			folders = append(folders, v.folder)
   811  		}
   812  
   813  		var openFiles []protocol.DocumentURI
   814  		for _, o := range s.Overlays() {
   815  			openFiles = append(openFiles, o.URI())
   816  		}
   817  		// Sort for determinism.
   818  		sort.Slice(openFiles, func(i, j int) bool {
   819  			return openFiles[i] < openFiles[j]
   820  		})
   821  
   822  		// TODO(rfindley): can we avoid running the go command (go env)
   823  		// synchronously to change processing? Can we assume that the env did not
   824  		// change, and derive go.work using a combination of the configured
   825  		// GOWORK value and filesystem?
   826  		defs, err := selectViewDefs(ctx, s, folders, openFiles)
   827  		if err != nil {
   828  			// Catastrophic failure, equivalent to a failure of session
   829  			// initialization and therefore should almost never happen. One
   830  			// scenario where this failure mode could occur is if some file
   831  			// permissions have changed preventing us from reading go.mod
   832  			// files.
   833  			//
   834  			// TODO(rfindley): consider surfacing this error more loudly. We
   835  			// could report a bug, but it's not really a bug.
   836  			event.Error(ctx, "selecting new views", err)
   837  		} else {
   838  			kept := make(map[*View]unit)
   839  			var newViews []*View
   840  			for _, def := range defs {
   841  				var newView *View
   842  				// Reuse existing view?
   843  				for _, v := range s.views {
   844  					if viewDefinitionsEqual(def, v.viewDefinition) {
   845  						newView = v
   846  						kept[v] = unit{}
   847  						break
   848  					}
   849  				}
   850  				if newView == nil {
   851  					v, _, release := s.createView(ctx, def)
   852  					release()
   853  					newView = v
   854  				}
   855  				newViews = append(newViews, newView)
   856  			}
   857  			for _, v := range s.views {
   858  				if _, ok := kept[v]; !ok {
   859  					v.shutdown()
   860  				}
   861  			}
   862  			s.views = newViews
   863  			s.viewMap = make(map[protocol.DocumentURI]*View)
   864  		}
   865  	}
   866  
   867  	// We only want to run fast-path diagnostics (i.e. diagnoseChangedFiles) once
   868  	// for each changed file, in its best view.
   869  	viewsToDiagnose := map[*View][]protocol.DocumentURI{}
   870  	for _, mod := range modifications {
   871  		v, err := s.viewOfLocked(ctx, mod.URI)
   872  		if err != nil {
   873  			// bestViewForURI only returns an error in the event of context
   874  			// cancellation. Since state changes should occur on an uncancellable
   875  			// context, an error here is a bug.
   876  			bug.Reportf("finding best view for change: %v", err)
   877  			continue
   878  		}
   879  		if v != nil {
   880  			viewsToDiagnose[v] = append(viewsToDiagnose[v], mod.URI)
   881  		}
   882  	}
   883  
   884  	// ...but changes may be relevant to other views, for example if they are
   885  	// changes to a shared package.
   886  	for _, v := range s.views {
   887  		_, release, needsDiagnosis := s.invalidateViewLocked(ctx, v, StateChange{Modifications: modifications, Files: changed})
   888  		release()
   889  
   890  		if needsDiagnosis || checkViews {
   891  			if _, ok := viewsToDiagnose[v]; !ok {
   892  				viewsToDiagnose[v] = nil
   893  			}
   894  		}
   895  	}
   896  
   897  	return viewsToDiagnose, nil
   898  }
   899  
   900  // ExpandModificationsToDirectories returns the set of changes with the
   901  // directory changes removed and expanded to include all of the files in
   902  // the directory.
   903  func (s *Session) ExpandModificationsToDirectories(ctx context.Context, changes []file.Modification) []file.Modification {
   904  	var snapshots []*Snapshot
   905  	s.viewMu.Lock()
   906  	for _, v := range s.views {
   907  		snapshot, release, err := v.Snapshot()
   908  		if err != nil {
   909  			continue // view is shut down; continue with others
   910  		}
   911  		defer release()
   912  		snapshots = append(snapshots, snapshot)
   913  	}
   914  	s.viewMu.Unlock()
   915  
   916  	// Expand the modification to any file we could care about, which we define
   917  	// to be any file observed by any of the snapshots.
   918  	//
   919  	// There may be other files in the directory, but if we haven't read them yet
   920  	// we don't need to invalidate them.
   921  	var result []file.Modification
   922  	for _, c := range changes {
   923  		expanded := make(map[protocol.DocumentURI]bool)
   924  		for _, snapshot := range snapshots {
   925  			for _, uri := range snapshot.filesInDir(c.URI) {
   926  				expanded[uri] = true
   927  			}
   928  		}
   929  		if len(expanded) == 0 {
   930  			result = append(result, c)
   931  		} else {
   932  			for uri := range expanded {
   933  				result = append(result, file.Modification{
   934  					URI:        uri,
   935  					Action:     c.Action,
   936  					LanguageID: "",
   937  					OnDisk:     c.OnDisk,
   938  					// changes to directories cannot include text or versions
   939  				})
   940  			}
   941  		}
   942  	}
   943  	return result
   944  }
   945  
   946  // updateOverlays updates the set of overlays and returns a map of any existing
   947  // overlay values that were replaced.
   948  //
   949  // Precondition: caller holds s.viewMu lock.
   950  // TODO(rfindley): move this to fs_overlay.go.
   951  func (fs *overlayFS) updateOverlays(ctx context.Context, changes []file.Modification) (map[protocol.DocumentURI]*overlay, error) {
   952  	fs.mu.Lock()
   953  	defer fs.mu.Unlock()
   954  
   955  	replaced := make(map[protocol.DocumentURI]*overlay)
   956  	for _, c := range changes {
   957  		o, ok := fs.overlays[c.URI]
   958  		if ok {
   959  			replaced[c.URI] = o
   960  		}
   961  
   962  		// If the file is not opened in an overlay and the change is on disk,
   963  		// there's no need to update an overlay. If there is an overlay, we
   964  		// may need to update the overlay's saved value.
   965  		if !ok && c.OnDisk {
   966  			continue
   967  		}
   968  
   969  		// Determine the file kind on open, otherwise, assume it has been cached.
   970  		var kind file.Kind
   971  		switch c.Action {
   972  		case file.Open:
   973  			kind = file.KindForLang(c.LanguageID)
   974  		default:
   975  			if !ok {
   976  				return nil, fmt.Errorf("updateOverlays: modifying unopened overlay %v", c.URI)
   977  			}
   978  			kind = o.kind
   979  		}
   980  
   981  		// Closing a file just deletes its overlay.
   982  		if c.Action == file.Close {
   983  			delete(fs.overlays, c.URI)
   984  			continue
   985  		}
   986  
   987  		// If the file is on disk, check if its content is the same as in the
   988  		// overlay. Saves and on-disk file changes don't come with the file's
   989  		// content.
   990  		text := c.Text
   991  		if text == nil && (c.Action == file.Save || c.OnDisk) {
   992  			if !ok {
   993  				return nil, fmt.Errorf("no known content for overlay for %s", c.Action)
   994  			}
   995  			text = o.content
   996  		}
   997  		// On-disk changes don't come with versions.
   998  		version := c.Version
   999  		if c.OnDisk || c.Action == file.Save {
  1000  			version = o.version
  1001  		}
  1002  		hash := file.HashOf(text)
  1003  		var sameContentOnDisk bool
  1004  		switch c.Action {
  1005  		case file.Delete:
  1006  			// Do nothing. sameContentOnDisk should be false.
  1007  		case file.Save:
  1008  			// Make sure the version and content (if present) is the same.
  1009  			if false && o.version != version { // Client no longer sends the version
  1010  				return nil, fmt.Errorf("updateOverlays: saving %s at version %v, currently at %v", c.URI, c.Version, o.version)
  1011  			}
  1012  			if c.Text != nil && o.hash != hash {
  1013  				return nil, fmt.Errorf("updateOverlays: overlay %s changed on save", c.URI)
  1014  			}
  1015  			sameContentOnDisk = true
  1016  		default:
  1017  			fh := mustReadFile(ctx, fs.delegate, c.URI)
  1018  			_, readErr := fh.Content()
  1019  			sameContentOnDisk = (readErr == nil && fh.Identity().Hash == hash)
  1020  		}
  1021  		o = &overlay{
  1022  			uri:     c.URI,
  1023  			version: version,
  1024  			content: text,
  1025  			kind:    kind,
  1026  			hash:    hash,
  1027  			saved:   sameContentOnDisk,
  1028  		}
  1029  
  1030  		// NOTE: previous versions of this code checked here that the overlay had a
  1031  		// view and file kind (but we don't know why).
  1032  
  1033  		fs.overlays[c.URI] = o
  1034  	}
  1035  
  1036  	return replaced, nil
  1037  }
  1038  
  1039  func mustReadFile(ctx context.Context, fs file.Source, uri protocol.DocumentURI) file.Handle {
  1040  	ctx = xcontext.Detach(ctx)
  1041  	fh, err := fs.ReadFile(ctx, uri)
  1042  	if err != nil {
  1043  		// ReadFile cannot fail with an uncancellable context.
  1044  		bug.Reportf("reading file failed unexpectedly: %v", err)
  1045  		return brokenFile{uri, err}
  1046  	}
  1047  	return fh
  1048  }
  1049  
  1050  // A brokenFile represents an unexpected failure to read a file.
  1051  type brokenFile struct {
  1052  	uri protocol.DocumentURI
  1053  	err error
  1054  }
  1055  
  1056  func (b brokenFile) URI() protocol.DocumentURI { return b.uri }
  1057  func (b brokenFile) Identity() file.Identity   { return file.Identity{URI: b.uri} }
  1058  func (b brokenFile) SameContentsOnDisk() bool  { return false }
  1059  func (b brokenFile) Version() int32            { return 0 }
  1060  func (b brokenFile) Content() ([]byte, error)  { return nil, b.err }
  1061  
  1062  // FileWatchingGlobPatterns returns a set of glob patterns that the client is
  1063  // required to watch for changes, and notify the server of them, in order to
  1064  // keep the server's state up to date.
  1065  //
  1066  // This set includes
  1067  //  1. all go.mod and go.work files in the workspace; and
  1068  //  2. for each Snapshot, its modules (or directory for ad-hoc views). In
  1069  //     module mode, this is the set of active modules (and for VS Code, all
  1070  //     workspace directories within them, due to golang/go#42348).
  1071  //
  1072  // The watch for workspace go.work and go.mod files in (1) is sufficient to
  1073  // capture changes to the repo structure that may affect the set of views.
  1074  // Whenever this set changes, we reload the workspace and invalidate memoized
  1075  // files.
  1076  //
  1077  // The watch for workspace directories in (2) should keep each View up to date,
  1078  // as it should capture any newly added/modified/deleted Go files.
  1079  //
  1080  // Patterns are returned as a set of protocol.RelativePatterns, since they can
  1081  // always be later translated to glob patterns (i.e. strings) if the client
  1082  // lacks relative pattern support. By convention, any pattern returned with
  1083  // empty baseURI should be served as a glob pattern.
  1084  //
  1085  // In general, we prefer to serve relative patterns, as they work better on
  1086  // most clients that support both, and do not have issues with Windows driver
  1087  // letter casing:
  1088  // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#relativePattern
  1089  //
  1090  // TODO(golang/go#57979): we need to reset the memoizedFS when a view changes.
  1091  // Consider the case where we incidentally read a file, then it moved outside
  1092  // of an active module, and subsequently changed: we would still observe the
  1093  // original file state.
  1094  func (s *Session) FileWatchingGlobPatterns(ctx context.Context) map[protocol.RelativePattern]unit {
  1095  	s.viewMu.Lock()
  1096  	defer s.viewMu.Unlock()
  1097  
  1098  	// Always watch files that may change the set of views.
  1099  	patterns := map[protocol.RelativePattern]unit{
  1100  		{Pattern: "**/*.{mod,work}"}: {},
  1101  	}
  1102  
  1103  	for _, view := range s.views {
  1104  		snapshot, release, err := view.Snapshot()
  1105  		if err != nil {
  1106  			continue // view is shut down; continue with others
  1107  		}
  1108  		for k, v := range snapshot.fileWatchingGlobPatterns() {
  1109  			patterns[k] = v
  1110  		}
  1111  		release()
  1112  	}
  1113  	return patterns
  1114  }
  1115  
  1116  // OrphanedFileDiagnostics reports diagnostics describing why open files have
  1117  // no packages or have only command-line-arguments packages.
  1118  //
  1119  // If the resulting diagnostic is nil, the file is either not orphaned or we
  1120  // can't produce a good diagnostic.
  1121  //
  1122  // The caller must not mutate the result.
  1123  func (s *Session) OrphanedFileDiagnostics(ctx context.Context) (map[protocol.DocumentURI][]*Diagnostic, error) {
  1124  	// Note: diagnostics holds a slice for consistency with other diagnostic
  1125  	// funcs.
  1126  	diagnostics := make(map[protocol.DocumentURI][]*Diagnostic)
  1127  
  1128  	byView := make(map[*View][]*overlay)
  1129  	for _, o := range s.Overlays() {
  1130  		uri := o.URI()
  1131  		snapshot, release, err := s.SnapshotOf(ctx, uri)
  1132  		if err != nil {
  1133  			// TODO(golang/go#57979): we have to use the .go suffix as an approximation for
  1134  			// file kind here, because we don't have access to Options if no View was
  1135  			// matched.
  1136  			//
  1137  			// But Options are really a property of Folder, not View, and we could
  1138  			// match a folder here.
  1139  			//
  1140  			// Refactor so that Folders are tracked independently of Views, and use
  1141  			// the correct options here to get the most accurate file kind.
  1142  			//
  1143  			// TODO(golang/go#57979): once we switch entirely to the zeroconfig
  1144  			// logic, we should use this diagnostic for the fallback case of
  1145  			// s.views[0] in the ViewOf logic.
  1146  			if errors.Is(err, errNoViews) {
  1147  				if strings.HasSuffix(string(uri), ".go") {
  1148  					if _, rng, ok := orphanedFileDiagnosticRange(ctx, s.parseCache, o); ok {
  1149  						diagnostics[uri] = []*Diagnostic{{
  1150  							URI:      uri,
  1151  							Range:    rng,
  1152  							Severity: protocol.SeverityWarning,
  1153  							Source:   ListError,
  1154  							Message:  fmt.Sprintf("No active builds contain %s: consider opening a new workspace folder containing it", uri.Path()),
  1155  						}}
  1156  					}
  1157  				}
  1158  				continue
  1159  			}
  1160  			return nil, err
  1161  		}
  1162  		v := snapshot.View()
  1163  		release()
  1164  		byView[v] = append(byView[v], o)
  1165  	}
  1166  
  1167  	for view, overlays := range byView {
  1168  		snapshot, release, err := view.Snapshot()
  1169  		if err != nil {
  1170  			continue // view is shutting down
  1171  		}
  1172  		defer release()
  1173  		diags, err := snapshot.orphanedFileDiagnostics(ctx, overlays)
  1174  		if err != nil {
  1175  			return nil, err
  1176  		}
  1177  		for _, d := range diags {
  1178  			diagnostics[d.URI] = append(diagnostics[d.URI], d)
  1179  		}
  1180  	}
  1181  	return diagnostics, nil
  1182  }