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