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.view.session.cache.fset,
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	m := s.getMetadata(id)
1118	return m == nil || !m.valid
1119}
1120
1121// updateIDForURIs adds the given ID to the set of known IDs for the given URI.
1122// Any existing invalid IDs are removed from the set of known IDs. IDs that are
1123// not "command-line-arguments" are preferred, so if a new ID comes in for a
1124// URI that previously only had "command-line-arguments", the new ID will
1125// replace the "command-line-arguments" ID.
1126func (s *snapshot) updateIDForURIs(id packageID, uris map[span.URI]struct{}) {
1127	s.mu.Lock()
1128	defer s.mu.Unlock()
1129
1130	for uri := range uris {
1131		// Collect the new set of IDs, preserving any valid existing IDs.
1132		newIDs := []packageID{id}
1133		for _, existingID := range s.ids[uri] {
1134			// Don't set duplicates of the same ID.
1135			if existingID == id {
1136				continue
1137			}
1138			// If the package previously only had a command-line-arguments ID,
1139			// delete the command-line-arguments workspace package.
1140			if source.IsCommandLineArguments(string(existingID)) {
1141				delete(s.workspacePackages, existingID)
1142				continue
1143			}
1144			// If the metadata for an existing ID is invalid, and we are
1145			// setting metadata for a new, valid ID--don't preserve the old ID.
1146			if m, ok := s.metadata[existingID]; !ok || !m.valid {
1147				continue
1148			}
1149			newIDs = append(newIDs, existingID)
1150		}
1151		sort.Slice(newIDs, func(i, j int) bool {
1152			return newIDs[i] < newIDs[j]
1153		})
1154		s.ids[uri] = newIDs
1155	}
1156}
1157
1158func (s *snapshot) isWorkspacePackage(id packageID) bool {
1159	s.mu.Lock()
1160	defer s.mu.Unlock()
1161
1162	_, ok := s.workspacePackages[id]
1163	return ok
1164}
1165
1166func (s *snapshot) FindFile(uri span.URI) source.VersionedFileHandle {
1167	f := s.view.getFile(uri)
1168
1169	s.mu.Lock()
1170	defer s.mu.Unlock()
1171
1172	return s.files[f.URI()]
1173}
1174
1175// GetVersionedFile returns a File for the given URI. If the file is unknown it
1176// is added to the managed set.
1177//
1178// GetVersionedFile succeeds even if the file does not exist. A non-nil error return
1179// indicates some type of internal error, for example if ctx is cancelled.
1180func (s *snapshot) GetVersionedFile(ctx context.Context, uri span.URI) (source.VersionedFileHandle, error) {
1181	f := s.view.getFile(uri)
1182
1183	s.mu.Lock()
1184	defer s.mu.Unlock()
1185	return s.getFileLocked(ctx, f)
1186}
1187
1188// GetFile implements the fileSource interface by wrapping GetVersionedFile.
1189func (s *snapshot) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
1190	return s.GetVersionedFile(ctx, uri)
1191}
1192
1193func (s *snapshot) getFileLocked(ctx context.Context, f *fileBase) (source.VersionedFileHandle, error) {
1194	if fh, ok := s.files[f.URI()]; ok {
1195		return fh, nil
1196	}
1197
1198	fh, err := s.view.session.cache.getFile(ctx, f.URI())
1199	if err != nil {
1200		return nil, err
1201	}
1202	closed := &closedFile{fh}
1203	s.files[f.URI()] = closed
1204	return closed, nil
1205}
1206
1207func (s *snapshot) IsOpen(uri span.URI) bool {
1208	s.mu.Lock()
1209	defer s.mu.Unlock()
1210	return s.isOpenLocked(uri)
1211
1212}
1213
1214func (s *snapshot) openFiles() []source.VersionedFileHandle {
1215	s.mu.Lock()
1216	defer s.mu.Unlock()
1217
1218	var open []source.VersionedFileHandle
1219	for _, fh := range s.files {
1220		if s.isOpenLocked(fh.URI()) {
1221			open = append(open, fh)
1222		}
1223	}
1224	return open
1225}
1226
1227func (s *snapshot) isOpenLocked(uri span.URI) bool {
1228	_, open := s.files[uri].(*overlay)
1229	return open
1230}
1231
1232func (s *snapshot) awaitLoaded(ctx context.Context) error {
1233	loadErr := s.awaitLoadedAllErrors(ctx)
1234
1235	s.mu.Lock()
1236	defer s.mu.Unlock()
1237
1238	// If we still have absolutely no metadata, check if the view failed to
1239	// initialize and return any errors.
1240	if s.useInvalidMetadata() && len(s.metadata) > 0 {
1241		return nil
1242	}
1243	for _, m := range s.metadata {
1244		if m.valid {
1245			return nil
1246		}
1247	}
1248	if loadErr != nil {
1249		return loadErr.MainError
1250	}
1251	return nil
1252}
1253
1254func (s *snapshot) GetCriticalError(ctx context.Context) *source.CriticalError {
1255	loadErr := s.awaitLoadedAllErrors(ctx)
1256	if loadErr != nil && errors.Is(loadErr.MainError, context.Canceled) {
1257		return nil
1258	}
1259
1260	// Even if packages didn't fail to load, we still may want to show
1261	// additional warnings.
1262	if loadErr == nil {
1263		wsPkgs, _ := s.WorkspacePackages(ctx)
1264		if msg := shouldShowAdHocPackagesWarning(s, wsPkgs); msg != "" {
1265			return &source.CriticalError{
1266				MainError: errors.New(msg),
1267			}
1268		}
1269		// Even if workspace packages were returned, there still may be an error
1270		// with the user's workspace layout. Workspace packages that only have the
1271		// ID "command-line-arguments" are usually a symptom of a bad workspace
1272		// configuration.
1273		if containsCommandLineArguments(wsPkgs) {
1274			return s.workspaceLayoutError(ctx)
1275		}
1276		return nil
1277	}
1278
1279	if errMsg := loadErr.MainError.Error(); strings.Contains(errMsg, "cannot find main module") || strings.Contains(errMsg, "go.mod file not found") {
1280		return s.workspaceLayoutError(ctx)
1281	}
1282	return loadErr
1283}
1284
1285const adHocPackagesWarning = `You are outside of a module and outside of $GOPATH/src.
1286If you are using modules, please open your editor to a directory in your module.
1287If you believe this warning is incorrect, please file an issue: https://github.com/golang/go/issues/new.`
1288
1289func shouldShowAdHocPackagesWarning(snapshot source.Snapshot, pkgs []source.Package) string {
1290	if snapshot.ValidBuildConfiguration() {
1291		return ""
1292	}
1293	for _, pkg := range pkgs {
1294		if len(pkg.MissingDependencies()) > 0 {
1295			return adHocPackagesWarning
1296		}
1297	}
1298	return ""
1299}
1300
1301func containsCommandLineArguments(pkgs []source.Package) bool {
1302	for _, pkg := range pkgs {
1303		if source.IsCommandLineArguments(pkg.ID()) {
1304			return true
1305		}
1306	}
1307	return false
1308}
1309
1310func (s *snapshot) awaitLoadedAllErrors(ctx context.Context) *source.CriticalError {
1311	// Do not return results until the snapshot's view has been initialized.
1312	s.AwaitInitialized(ctx)
1313
1314	// TODO(rstambler): Should we be more careful about returning the
1315	// initialization error? Is it possible for the initialization error to be
1316	// corrected without a successful reinitialization?
1317	s.mu.Lock()
1318	initializedErr := s.initializedErr
1319	s.mu.Unlock()
1320	if initializedErr != nil {
1321		return initializedErr
1322	}
1323
1324	if ctx.Err() != nil {
1325		return &source.CriticalError{MainError: ctx.Err()}
1326	}
1327
1328	if err := s.reloadWorkspace(ctx); err != nil {
1329		diags, _ := s.extractGoCommandErrors(ctx, err.Error())
1330		return &source.CriticalError{
1331			MainError: err,
1332			DiagList:  diags,
1333		}
1334	}
1335	if err := s.reloadOrphanedFiles(ctx); err != nil {
1336		diags, _ := s.extractGoCommandErrors(ctx, err.Error())
1337		return &source.CriticalError{
1338			MainError: err,
1339			DiagList:  diags,
1340		}
1341	}
1342	return nil
1343}
1344
1345func (s *snapshot) getInitializationError(ctx context.Context) *source.CriticalError {
1346	s.mu.Lock()
1347	defer s.mu.Unlock()
1348
1349	return s.initializedErr
1350}
1351
1352func (s *snapshot) AwaitInitialized(ctx context.Context) {
1353	select {
1354	case <-ctx.Done():
1355		return
1356	case <-s.view.initialWorkspaceLoad:
1357	}
1358	// We typically prefer to run something as intensive as the IWL without
1359	// blocking. I'm not sure if there is a way to do that here.
1360	s.initialize(ctx, false)
1361}
1362
1363// reloadWorkspace reloads the metadata for all invalidated workspace packages.
1364func (s *snapshot) reloadWorkspace(ctx context.Context) error {
1365	// See which of the workspace packages are missing metadata.
1366	s.mu.Lock()
1367	missingMetadata := len(s.workspacePackages) == 0 || len(s.metadata) == 0
1368	pkgPathSet := map[packagePath]struct{}{}
1369	for id, pkgPath := range s.workspacePackages {
1370		if m, ok := s.metadata[id]; ok && m.valid {
1371			continue
1372		}
1373		missingMetadata = true
1374
1375		// Don't try to reload "command-line-arguments" directly.
1376		if source.IsCommandLineArguments(string(pkgPath)) {
1377			continue
1378		}
1379		pkgPathSet[pkgPath] = struct{}{}
1380	}
1381	s.mu.Unlock()
1382
1383	// If the view's build configuration is invalid, we cannot reload by
1384	// package path. Just reload the directory instead.
1385	if missingMetadata && !s.ValidBuildConfiguration() {
1386		return s.load(ctx, false, viewLoadScope("LOAD_INVALID_VIEW"))
1387	}
1388
1389	if len(pkgPathSet) == 0 {
1390		return nil
1391	}
1392
1393	var pkgPaths []interface{}
1394	for pkgPath := range pkgPathSet {
1395		pkgPaths = append(pkgPaths, pkgPath)
1396	}
1397	return s.load(ctx, false, pkgPaths...)
1398}
1399
1400func (s *snapshot) reloadOrphanedFiles(ctx context.Context) error {
1401	// When we load ./... or a package path directly, we may not get packages
1402	// that exist only in overlays. As a workaround, we search all of the files
1403	// available in the snapshot and reload their metadata individually using a
1404	// file= query if the metadata is unavailable.
1405	files := s.orphanedFiles()
1406
1407	// Files without a valid package declaration can't be loaded. Don't try.
1408	var scopes []interface{}
1409	for _, file := range files {
1410		pgf, err := s.ParseGo(ctx, file, source.ParseHeader)
1411		if err != nil {
1412			continue
1413		}
1414		if !pgf.File.Package.IsValid() {
1415			continue
1416		}
1417		scopes = append(scopes, fileURI(file.URI()))
1418	}
1419
1420	if len(scopes) == 0 {
1421		return nil
1422	}
1423
1424	// The regtests match this exact log message, keep them in sync.
1425	event.Log(ctx, "reloadOrphanedFiles reloading", tag.Query.Of(scopes))
1426	err := s.load(ctx, false, scopes...)
1427
1428	// If we failed to load some files, i.e. they have no metadata,
1429	// mark the failures so we don't bother retrying until the file's
1430	// content changes.
1431	//
1432	// TODO(rstambler): This may be an overestimate if the load stopped
1433	// early for an unrelated errors. Add a fallback?
1434	//
1435	// Check for context cancellation so that we don't incorrectly mark files
1436	// as unloadable, but don't return before setting all workspace packages.
1437	if ctx.Err() == nil && err != nil {
1438		event.Error(ctx, "reloadOrphanedFiles: failed to load", err, tag.Query.Of(scopes))
1439		s.mu.Lock()
1440		for _, scope := range scopes {
1441			uri := span.URI(scope.(fileURI))
1442			if s.noValidMetadataForURILocked(uri) {
1443				s.unloadableFiles[uri] = struct{}{}
1444			}
1445		}
1446		s.mu.Unlock()
1447	}
1448	return nil
1449}
1450
1451func (s *snapshot) orphanedFiles() []source.VersionedFileHandle {
1452	s.mu.Lock()
1453	defer s.mu.Unlock()
1454
1455	var files []source.VersionedFileHandle
1456	for uri, fh := range s.files {
1457		// Don't try to reload metadata for go.mod files.
1458		if fh.Kind() != source.Go {
1459			continue
1460		}
1461		// If the URI doesn't belong to this view, then it's not in a workspace
1462		// package and should not be reloaded directly.
1463		if !contains(s.view.session.viewsOf(uri), s.view) {
1464			continue
1465		}
1466		// If the file is not open and is in a vendor directory, don't treat it
1467		// like a workspace package.
1468		if _, ok := fh.(*overlay); !ok && inVendor(uri) {
1469			continue
1470		}
1471		// Don't reload metadata for files we've already deemed unloadable.
1472		if _, ok := s.unloadableFiles[uri]; ok {
1473			continue
1474		}
1475		if s.noValidMetadataForURILocked(uri) {
1476			files = append(files, fh)
1477		}
1478	}
1479	return files
1480}
1481
1482func contains(views []*View, view *View) bool {
1483	for _, v := range views {
1484		if v == view {
1485			return true
1486		}
1487	}
1488	return false
1489}
1490
1491func inVendor(uri span.URI) bool {
1492	if !strings.Contains(string(uri), "/vendor/") {
1493		return false
1494	}
1495	// Only packages in _subdirectories_ of /vendor/ are considered vendored
1496	// (/vendor/a/foo.go is vendored, /vendor/foo.go is not).
1497	split := strings.Split(string(uri), "/vendor/")
1498	if len(split) < 2 {
1499		return false
1500	}
1501	return strings.Contains(split[1], "/")
1502}
1503
1504func generationName(v *View, snapshotID uint64) string {
1505	return fmt.Sprintf("v%v/%v", v.id, snapshotID)
1506}
1507
1508// checkSnapshotLocked verifies that some invariants are preserved on the
1509// snapshot.
1510func checkSnapshotLocked(ctx context.Context, s *snapshot) {
1511	// Check that every go file for a workspace package is identified as
1512	// belonging to that workspace package.
1513	for wsID := range s.workspacePackages {
1514		if m, ok := s.metadata[wsID]; ok {
1515			for _, uri := range m.goFiles {
1516				found := false
1517				for _, id := range s.ids[uri] {
1518					if id == wsID {
1519						found = true
1520						break
1521					}
1522				}
1523				if !found {
1524					log.Error.Logf(ctx, "workspace package %v not associated with %v", wsID, uri)
1525				}
1526			}
1527		}
1528	}
1529}
1530
1531func (s *snapshot) clone(ctx, bgCtx context.Context, changes map[span.URI]*fileChange, forceReloadMetadata bool) (*snapshot, bool) {
1532	var vendorChanged bool
1533	newWorkspace, workspaceChanged, workspaceReload := s.workspace.invalidate(ctx, changes)
1534
1535	s.mu.Lock()
1536	defer s.mu.Unlock()
1537
1538	checkSnapshotLocked(ctx, s)
1539
1540	newGen := s.view.session.cache.store.Generation(generationName(s.view, s.id+1))
1541	bgCtx, cancel := context.WithCancel(bgCtx)
1542	result := &snapshot{
1543		id:                s.id + 1,
1544		generation:        newGen,
1545		view:              s.view,
1546		backgroundCtx:     bgCtx,
1547		cancel:            cancel,
1548		builtin:           s.builtin,
1549		initializeOnce:    s.initializeOnce,
1550		initializedErr:    s.initializedErr,
1551		ids:               make(map[span.URI][]packageID, len(s.ids)),
1552		importedBy:        make(map[packageID][]packageID, len(s.importedBy)),
1553		metadata:          make(map[packageID]*knownMetadata, len(s.metadata)),
1554		packages:          make(map[packageKey]*packageHandle, len(s.packages)),
1555		actions:           make(map[actionKey]*actionHandle, len(s.actions)),
1556		files:             make(map[span.URI]source.VersionedFileHandle, len(s.files)),
1557		goFiles:           make(map[parseKey]*parseGoHandle, len(s.goFiles)),
1558		workspacePackages: make(map[packageID]packagePath, len(s.workspacePackages)),
1559		unloadableFiles:   make(map[span.URI]struct{}, len(s.unloadableFiles)),
1560		parseModHandles:   make(map[span.URI]*parseModHandle, len(s.parseModHandles)),
1561		modTidyHandles:    make(map[span.URI]*modTidyHandle, len(s.modTidyHandles)),
1562		modWhyHandles:     make(map[span.URI]*modWhyHandle, len(s.modWhyHandles)),
1563		knownSubdirs:      make(map[span.URI]struct{}, len(s.knownSubdirs)),
1564		workspace:         newWorkspace,
1565	}
1566
1567	if !workspaceChanged && s.workspaceDirHandle != nil {
1568		result.workspaceDirHandle = s.workspaceDirHandle
1569		newGen.Inherit(s.workspaceDirHandle)
1570	}
1571
1572	// Copy all of the FileHandles.
1573	for k, v := range s.files {
1574		result.files[k] = v
1575	}
1576
1577	// Copy the set of unloadable files.
1578	for k, v := range s.unloadableFiles {
1579		result.unloadableFiles[k] = v
1580	}
1581	// Copy all of the modHandles.
1582	for k, v := range s.parseModHandles {
1583		result.parseModHandles[k] = v
1584	}
1585
1586	for k, v := range s.goFiles {
1587		if _, ok := changes[k.file.URI]; ok {
1588			continue
1589		}
1590		newGen.Inherit(v.handle)
1591		result.goFiles[k] = v
1592	}
1593
1594	// Copy all of the go.mod-related handles. They may be invalidated later,
1595	// so we inherit them at the end of the function.
1596	for k, v := range s.modTidyHandles {
1597		if _, ok := changes[k]; ok {
1598			continue
1599		}
1600		result.modTidyHandles[k] = v
1601	}
1602	for k, v := range s.modWhyHandles {
1603		if _, ok := changes[k]; ok {
1604			continue
1605		}
1606		result.modWhyHandles[k] = v
1607	}
1608
1609	// Add all of the known subdirectories, but don't update them for the
1610	// changed files. We need to rebuild the workspace module to know the
1611	// true set of known subdirectories, but we don't want to do that in clone.
1612	for k, v := range s.knownSubdirs {
1613		result.knownSubdirs[k] = v
1614	}
1615	for _, c := range changes {
1616		result.unprocessedSubdirChanges = append(result.unprocessedSubdirChanges, c)
1617	}
1618
1619	// directIDs keeps track of package IDs that have directly changed.
1620	// It maps id->invalidateMetadata.
1621	directIDs := map[packageID]bool{}
1622
1623	// Invalidate all package metadata if the workspace module has changed.
1624	if workspaceReload {
1625		for k := range s.metadata {
1626			directIDs[k] = true
1627		}
1628	}
1629
1630	changedPkgNames := map[packageID]struct{}{}
1631	anyImportDeleted := false
1632	for uri, change := range changes {
1633		// Maybe reinitialize the view if we see a change in the vendor
1634		// directory.
1635		if inVendor(uri) {
1636			vendorChanged = true
1637		}
1638
1639		// The original FileHandle for this URI is cached on the snapshot.
1640		originalFH := s.files[uri]
1641
1642		// Check if the file's package name or imports have changed,
1643		// and if so, invalidate this file's packages' metadata.
1644		var shouldInvalidateMetadata, pkgNameChanged, importDeleted bool
1645		if !isGoMod(uri) {
1646			shouldInvalidateMetadata, pkgNameChanged, importDeleted = s.shouldInvalidateMetadata(ctx, result, originalFH, change.fileHandle)
1647		}
1648		invalidateMetadata := forceReloadMetadata || workspaceReload || shouldInvalidateMetadata
1649		anyImportDeleted = anyImportDeleted || importDeleted
1650
1651		// Mark all of the package IDs containing the given file.
1652		// TODO: if the file has moved into a new package, we should invalidate that too.
1653		filePackageIDs := guessPackageIDsForURI(uri, s.ids)
1654		if pkgNameChanged {
1655			for _, id := range filePackageIDs {
1656				changedPkgNames[id] = struct{}{}
1657			}
1658		}
1659		for _, id := range filePackageIDs {
1660			directIDs[id] = directIDs[id] || invalidateMetadata
1661		}
1662
1663		// Invalidate the previous modTidyHandle if any of the files have been
1664		// saved or if any of the metadata has been invalidated.
1665		if invalidateMetadata || fileWasSaved(originalFH, change.fileHandle) {
1666			// TODO(rstambler): Only delete mod handles for which the
1667			// withoutURI is relevant.
1668			for k := range s.modTidyHandles {
1669				delete(result.modTidyHandles, k)
1670			}
1671			for k := range s.modWhyHandles {
1672				delete(result.modWhyHandles, k)
1673			}
1674		}
1675		if isGoMod(uri) {
1676			delete(result.parseModHandles, uri)
1677		}
1678		// Handle the invalidated file; it may have new contents or not exist.
1679		if !change.exists {
1680			delete(result.files, uri)
1681		} else {
1682			result.files[uri] = change.fileHandle
1683		}
1684
1685		// Make sure to remove the changed file from the unloadable set.
1686		delete(result.unloadableFiles, uri)
1687	}
1688
1689	// Deleting an import can cause list errors due to import cycles to be
1690	// resolved. The best we can do without parsing the list error message is to
1691	// hope that list errors may have been resolved by a deleted import.
1692	//
1693	// We could do better by parsing the list error message. We already do this
1694	// to assign a better range to the list error, but for such critical
1695	// functionality as metadata, it's better to be conservative until it proves
1696	// impractical.
1697	//
1698	// We could also do better by looking at which imports were deleted and
1699	// trying to find cycles they are involved in. This fails when the file goes
1700	// from an unparseable state to a parseable state, as we don't have a
1701	// starting point to compare with.
1702	if anyImportDeleted {
1703		for id, metadata := range s.metadata {
1704			if len(metadata.errors) > 0 {
1705				directIDs[id] = true
1706			}
1707		}
1708	}
1709
1710	// Invalidate reverse dependencies too.
1711	// TODO(heschi): figure out the locking model and use transitiveReverseDeps?
1712	// idsToInvalidate keeps track of transitive reverse dependencies.
1713	// If an ID is present in the map, invalidate its types.
1714	// If an ID's value is true, invalidate its metadata too.
1715	idsToInvalidate := map[packageID]bool{}
1716	var addRevDeps func(packageID, bool)
1717	addRevDeps = func(id packageID, invalidateMetadata bool) {
1718		current, seen := idsToInvalidate[id]
1719		newInvalidateMetadata := current || invalidateMetadata
1720
1721		// If we've already seen this ID, and the value of invalidate
1722		// metadata has not changed, we can return early.
1723		if seen && current == newInvalidateMetadata {
1724			return
1725		}
1726		idsToInvalidate[id] = newInvalidateMetadata
1727		for _, rid := range s.getImportedByLocked(id) {
1728			addRevDeps(rid, invalidateMetadata)
1729		}
1730	}
1731	for id, invalidateMetadata := range directIDs {
1732		addRevDeps(id, invalidateMetadata)
1733	}
1734
1735	// Copy the package type information.
1736	for k, v := range s.packages {
1737		if _, ok := idsToInvalidate[k.id]; ok {
1738			continue
1739		}
1740		newGen.Inherit(v.handle)
1741		result.packages[k] = v
1742	}
1743	// Copy the package analysis information.
1744	for k, v := range s.actions {
1745		if _, ok := idsToInvalidate[k.pkg.id]; ok {
1746			continue
1747		}
1748		newGen.Inherit(v.handle)
1749		result.actions[k] = v
1750	}
1751
1752	// If the workspace mode has changed, we must delete all metadata, as it
1753	// is unusable and may produce confusing or incorrect diagnostics.
1754	// If a file has been deleted, we must delete metadata all packages
1755	// containing that file.
1756	workspaceModeChanged := s.workspaceMode() != result.workspaceMode()
1757	skipID := map[packageID]bool{}
1758	for _, c := range changes {
1759		if c.exists {
1760			continue
1761		}
1762		// The file has been deleted.
1763		if ids, ok := s.ids[c.fileHandle.URI()]; ok {
1764			for _, id := range ids {
1765				skipID[id] = true
1766			}
1767		}
1768	}
1769
1770	// Collect all of the IDs that are reachable from the workspace packages.
1771	// Any unreachable IDs will have their metadata deleted outright.
1772	reachableID := map[packageID]bool{}
1773	var addForwardDeps func(packageID)
1774	addForwardDeps = func(id packageID) {
1775		if reachableID[id] {
1776			return
1777		}
1778		reachableID[id] = true
1779		m, ok := s.metadata[id]
1780		if !ok {
1781			return
1782		}
1783		for _, depID := range m.deps {
1784			addForwardDeps(depID)
1785		}
1786	}
1787	for id := range s.workspacePackages {
1788		addForwardDeps(id)
1789	}
1790
1791	// Copy the URI to package ID mappings, skipping only those URIs whose
1792	// metadata will be reloaded in future calls to load.
1793	deleteInvalidMetadata := forceReloadMetadata || workspaceModeChanged
1794	idsInSnapshot := map[packageID]bool{} // track all known IDs
1795	for uri, ids := range s.ids {
1796		for _, id := range ids {
1797			invalidateMetadata := idsToInvalidate[id]
1798			if skipID[id] || (invalidateMetadata && deleteInvalidMetadata) {
1799				continue
1800			}
1801			// The ID is not reachable from any workspace package, so it should
1802			// be deleted.
1803			if !reachableID[id] {
1804				continue
1805			}
1806			idsInSnapshot[id] = true
1807			result.ids[uri] = append(result.ids[uri], id)
1808		}
1809	}
1810
1811	// Copy the package metadata. We only need to invalidate packages directly
1812	// containing the affected file, and only if it changed in a relevant way.
1813	for k, v := range s.metadata {
1814		if !idsInSnapshot[k] {
1815			// Delete metadata for IDs that are no longer reachable from files
1816			// in the snapshot.
1817			continue
1818		}
1819		invalidateMetadata := idsToInvalidate[k]
1820		// Mark invalidated metadata rather than deleting it outright.
1821		result.metadata[k] = &knownMetadata{
1822			metadata:   v.metadata,
1823			valid:      v.valid && !invalidateMetadata,
1824			shouldLoad: v.shouldLoad || invalidateMetadata,
1825		}
1826	}
1827	// Copy the URI to package ID mappings, skipping only those URIs whose
1828	// metadata will be reloaded in future calls to load.
1829	for k, ids := range s.ids {
1830		var newIDs []packageID
1831		for _, id := range ids {
1832			if invalidateMetadata, ok := idsToInvalidate[id]; invalidateMetadata && ok {
1833				continue
1834			}
1835			newIDs = append(newIDs, id)
1836		}
1837		if len(newIDs) != 0 {
1838			result.ids[k] = newIDs
1839		}
1840	}
1841
1842	// Copy the set of initially loaded packages.
1843	for id, pkgPath := range s.workspacePackages {
1844		// Packages with the id "command-line-arguments" are generated by the
1845		// go command when the user is outside of GOPATH and outside of a
1846		// module. Do not cache them as workspace packages for longer than
1847		// necessary.
1848		if source.IsCommandLineArguments(string(id)) {
1849			if invalidateMetadata, ok := idsToInvalidate[id]; invalidateMetadata && ok {
1850				continue
1851			}
1852		}
1853
1854		// If all the files we know about in a package have been deleted,
1855		// the package is gone and we should no longer try to load it.
1856		if m := s.metadata[id]; m != nil {
1857			hasFiles := false
1858			for _, uri := range s.metadata[id].goFiles {
1859				// For internal tests, we need _test files, not just the normal
1860				// ones. External tests only have _test files, but we can check
1861				// them anyway.
1862				if m.forTest != "" && !strings.HasSuffix(string(uri), "_test.go") {
1863					continue
1864				}
1865				if _, ok := result.files[uri]; ok {
1866					hasFiles = true
1867					break
1868				}
1869			}
1870			if !hasFiles {
1871				continue
1872			}
1873		}
1874
1875		// If the package name of a file in the package has changed, it's
1876		// possible that the package ID may no longer exist. Delete it from
1877		// the set of workspace packages, on the assumption that we will add it
1878		// back when the relevant files are reloaded.
1879		if _, ok := changedPkgNames[id]; ok {
1880			continue
1881		}
1882
1883		result.workspacePackages[id] = pkgPath
1884	}
1885
1886	// Inherit all of the go.mod-related handles.
1887	for _, v := range result.modTidyHandles {
1888		newGen.Inherit(v.handle)
1889	}
1890	for _, v := range result.modWhyHandles {
1891		newGen.Inherit(v.handle)
1892	}
1893	for _, v := range result.parseModHandles {
1894		newGen.Inherit(v.handle)
1895	}
1896	// Don't bother copying the importedBy graph,
1897	// as it changes each time we update metadata.
1898
1899	// If the snapshot's workspace mode has changed, the packages loaded using
1900	// the previous mode are no longer relevant, so clear them out.
1901	if workspaceModeChanged {
1902		result.workspacePackages = map[packageID]packagePath{}
1903	}
1904
1905	// The snapshot may need to be reinitialized.
1906	if workspaceReload || vendorChanged {
1907		if workspaceChanged || result.initializedErr != nil {
1908			result.initializeOnce = &sync.Once{}
1909		}
1910	}
1911	return result, workspaceChanged
1912}
1913
1914// guessPackageIDsForURI returns all packages related to uri. If we haven't
1915// seen this URI before, we guess based on files in the same directory. This
1916// is of course incorrect in build systems where packages are not organized by
1917// directory.
1918func guessPackageIDsForURI(uri span.URI, known map[span.URI][]packageID) []packageID {
1919	packages := known[uri]
1920	if len(packages) > 0 {
1921		// We've seen this file before.
1922		return packages
1923	}
1924	// This is a file we don't yet know about. Guess relevant packages by
1925	// considering files in the same directory.
1926
1927	// Cache of FileInfo to avoid unnecessary stats for multiple files in the
1928	// same directory.
1929	stats := make(map[string]struct {
1930		os.FileInfo
1931		error
1932	})
1933	getInfo := func(dir string) (os.FileInfo, error) {
1934		if res, ok := stats[dir]; ok {
1935			return res.FileInfo, res.error
1936		}
1937		fi, err := os.Stat(dir)
1938		stats[dir] = struct {
1939			os.FileInfo
1940			error
1941		}{fi, err}
1942		return fi, err
1943	}
1944	dir := filepath.Dir(uri.Filename())
1945	fi, err := getInfo(dir)
1946	if err != nil {
1947		return nil
1948	}
1949
1950	// Aggregate all possibly relevant package IDs.
1951	var found []packageID
1952	for knownURI, ids := range known {
1953		knownDir := filepath.Dir(knownURI.Filename())
1954		knownFI, err := getInfo(knownDir)
1955		if err != nil {
1956			continue
1957		}
1958		if os.SameFile(fi, knownFI) {
1959			found = append(found, ids...)
1960		}
1961	}
1962	return found
1963}
1964
1965// fileWasSaved reports whether the FileHandle passed in has been saved. It
1966// accomplishes this by checking to see if the original and current FileHandles
1967// are both overlays, and if the current FileHandle is saved while the original
1968// FileHandle was not saved.
1969func fileWasSaved(originalFH, currentFH source.FileHandle) bool {
1970	c, ok := currentFH.(*overlay)
1971	if !ok || c == nil {
1972		return true
1973	}
1974	o, ok := originalFH.(*overlay)
1975	if !ok || o == nil {
1976		return c.saved
1977	}
1978	return !o.saved && c.saved
1979}
1980
1981// shouldInvalidateMetadata reparses a file's package and import declarations to
1982// determine if the file requires a metadata reload.
1983func (s *snapshot) shouldInvalidateMetadata(ctx context.Context, newSnapshot *snapshot, originalFH, currentFH source.FileHandle) (invalidate, pkgNameChanged, importDeleted bool) {
1984	if originalFH == nil {
1985		return true, false, false
1986	}
1987	// If the file hasn't changed, there's no need to reload.
1988	if originalFH.FileIdentity() == currentFH.FileIdentity() {
1989		return false, false, false
1990	}
1991	// Get the original and current parsed files in order to check package name
1992	// and imports. Use the new snapshot to parse to avoid modifying the
1993	// current snapshot.
1994	original, originalErr := newSnapshot.ParseGo(ctx, originalFH, source.ParseHeader)
1995	current, currentErr := newSnapshot.ParseGo(ctx, currentFH, source.ParseHeader)
1996	if originalErr != nil || currentErr != nil {
1997		return (originalErr == nil) != (currentErr == nil), false, (currentErr != nil) // we don't know if an import was deleted
1998	}
1999	// Check if the package's metadata has changed. The cases handled are:
2000	//    1. A package's name has changed
2001	//    2. A file's imports have changed
2002	if original.File.Name.Name != current.File.Name.Name {
2003		invalidate = true
2004		pkgNameChanged = true
2005	}
2006	origImportSet := make(map[string]struct{})
2007	for _, importSpec := range original.File.Imports {
2008		origImportSet[importSpec.Path.Value] = struct{}{}
2009	}
2010	curImportSet := make(map[string]struct{})
2011	for _, importSpec := range current.File.Imports {
2012		curImportSet[importSpec.Path.Value] = struct{}{}
2013	}
2014	// If any of the current imports were not in the original imports.
2015	for path := range curImportSet {
2016		if _, ok := origImportSet[path]; ok {
2017			delete(origImportSet, path)
2018			continue
2019		}
2020		// If the import path is obviously not valid, we can skip reloading
2021		// metadata. For now, valid means properly quoted and without a
2022		// terminal slash.
2023		if isBadImportPath(path) {
2024			continue
2025		}
2026		invalidate = true
2027	}
2028
2029	for path := range origImportSet {
2030		if !isBadImportPath(path) {
2031			invalidate = true
2032			importDeleted = true
2033		}
2034	}
2035
2036	if !invalidate {
2037		invalidate = magicCommentsChanged(original.File, current.File)
2038	}
2039	return invalidate, pkgNameChanged, importDeleted
2040}
2041
2042func magicCommentsChanged(original *ast.File, current *ast.File) bool {
2043	oldComments := extractMagicComments(original)
2044	newComments := extractMagicComments(current)
2045	if len(oldComments) != len(newComments) {
2046		return true
2047	}
2048	for i := range oldComments {
2049		if oldComments[i] != newComments[i] {
2050			return true
2051		}
2052	}
2053	return false
2054}
2055
2056func isBadImportPath(path string) bool {
2057	path, err := strconv.Unquote(path)
2058	if err != nil {
2059		return true
2060	}
2061	if path == "" {
2062		return true
2063	}
2064	if path[len(path)-1] == '/' {
2065		return true
2066	}
2067	return false
2068}
2069
2070var buildConstraintOrEmbedRe = regexp.MustCompile(`^//(go:embed|go:build|\s*\+build).*`)
2071
2072// extractMagicComments finds magic comments that affect metadata in f.
2073func extractMagicComments(f *ast.File) []string {
2074	var results []string
2075	for _, cg := range f.Comments {
2076		for _, c := range cg.List {
2077			if buildConstraintOrEmbedRe.MatchString(c.Text) {
2078				results = append(results, c.Text)
2079			}
2080		}
2081	}
2082	return results
2083}
2084
2085func (s *snapshot) BuiltinFile(ctx context.Context) (*source.ParsedGoFile, error) {
2086	s.AwaitInitialized(ctx)
2087
2088	s.mu.Lock()
2089	builtin := s.builtin
2090	s.mu.Unlock()
2091
2092	if builtin == "" {
2093		return nil, errors.Errorf("no builtin package for view %s", s.view.name)
2094	}
2095
2096	fh, err := s.GetFile(ctx, builtin)
2097	if err != nil {
2098		return nil, err
2099	}
2100	return s.ParseGo(ctx, fh, source.ParseFull)
2101}
2102
2103func (s *snapshot) IsBuiltin(ctx context.Context, uri span.URI) bool {
2104	s.mu.Lock()
2105	defer s.mu.Unlock()
2106	// We should always get the builtin URI in a canonical form, so use simple
2107	// string comparison here. span.CompareURI is too expensive.
2108	return uri == s.builtin
2109}
2110
2111func (s *snapshot) setBuiltin(path string) {
2112	s.mu.Lock()
2113	defer s.mu.Unlock()
2114
2115	s.builtin = span.URIFromPath(path)
2116}
2117
2118// BuildGoplsMod generates a go.mod file for all modules in the workspace. It
2119// bypasses any existing gopls.mod.
2120func (s *snapshot) BuildGoplsMod(ctx context.Context) (*modfile.File, error) {
2121	allModules, err := findModules(s.view.folder, pathExcludedByFilterFunc(s.view.rootURI.Filename(), s.view.gomodcache, s.View().Options()), 0)
2122	if err != nil {
2123		return nil, err
2124	}
2125	return buildWorkspaceModFile(ctx, allModules, s)
2126}
2127
2128// TODO(rfindley): move this to workspacemodule.go
2129func buildWorkspaceModFile(ctx context.Context, modFiles map[span.URI]struct{}, fs source.FileSource) (*modfile.File, error) {
2130	file := &modfile.File{}
2131	file.AddModuleStmt("gopls-workspace")
2132	// Track the highest Go version, to be set on the workspace module.
2133	// Fall back to 1.12 -- old versions insist on having some version.
2134	goVersion := "1.12"
2135
2136	paths := map[string]span.URI{}
2137	excludes := map[string][]string{}
2138	var sortedModURIs []span.URI
2139	for uri := range modFiles {
2140		sortedModURIs = append(sortedModURIs, uri)
2141	}
2142	sort.Slice(sortedModURIs, func(i, j int) bool {
2143		return sortedModURIs[i] < sortedModURIs[j]
2144	})
2145	for _, modURI := range sortedModURIs {
2146		fh, err := fs.GetFile(ctx, modURI)
2147		if err != nil {
2148			return nil, err
2149		}
2150		content, err := fh.Read()
2151		if err != nil {
2152			return nil, err
2153		}
2154		parsed, err := modfile.Parse(fh.URI().Filename(), content, nil)
2155		if err != nil {
2156			return nil, err
2157		}
2158		if file == nil || parsed.Module == nil {
2159			return nil, fmt.Errorf("no module declaration for %s", modURI)
2160		}
2161		if parsed.Go != nil && semver.Compare(goVersion, parsed.Go.Version) < 0 {
2162			goVersion = parsed.Go.Version
2163		}
2164		path := parsed.Module.Mod.Path
2165		if _, ok := paths[path]; ok {
2166			return nil, fmt.Errorf("found module %q twice in the workspace", path)
2167		}
2168		paths[path] = modURI
2169		// If the module's path includes a major version, we expect it to have
2170		// a matching major version.
2171		_, majorVersion, _ := module.SplitPathVersion(path)
2172		if majorVersion == "" {
2173			majorVersion = "/v0"
2174		}
2175		majorVersion = strings.TrimLeft(majorVersion, "/.") // handle gopkg.in versions
2176		file.AddNewRequire(path, source.WorkspaceModuleVersion(majorVersion), false)
2177		if err := file.AddReplace(path, "", dirURI(modURI).Filename(), ""); err != nil {
2178			return nil, err
2179		}
2180		for _, exclude := range parsed.Exclude {
2181			excludes[exclude.Mod.Path] = append(excludes[exclude.Mod.Path], exclude.Mod.Version)
2182		}
2183	}
2184	if goVersion != "" {
2185		file.AddGoStmt(goVersion)
2186	}
2187	// Go back through all of the modules to handle any of their replace
2188	// statements.
2189	for _, modURI := range sortedModURIs {
2190		fh, err := fs.GetFile(ctx, modURI)
2191		if err != nil {
2192			return nil, err
2193		}
2194		content, err := fh.Read()
2195		if err != nil {
2196			return nil, err
2197		}
2198		parsed, err := modfile.Parse(fh.URI().Filename(), content, nil)
2199		if err != nil {
2200			return nil, err
2201		}
2202		// If any of the workspace modules have replace directives, they need
2203		// to be reflected in the workspace module.
2204		for _, rep := range parsed.Replace {
2205			// Don't replace any modules that are in our workspace--we should
2206			// always use the version in the workspace.
2207			if _, ok := paths[rep.Old.Path]; ok {
2208				continue
2209			}
2210			newPath := rep.New.Path
2211			newVersion := rep.New.Version
2212			// If a replace points to a module in the workspace, make sure we
2213			// direct it to version of the module in the workspace.
2214			if m, ok := paths[rep.New.Path]; ok {
2215				newPath = dirURI(m).Filename()
2216				newVersion = ""
2217			} else if rep.New.Version == "" && !filepath.IsAbs(rep.New.Path) {
2218				// Make any relative paths absolute.
2219				newPath = filepath.Join(dirURI(modURI).Filename(), rep.New.Path)
2220			}
2221			if err := file.AddReplace(rep.Old.Path, rep.Old.Version, newPath, newVersion); err != nil {
2222				return nil, err
2223			}
2224		}
2225	}
2226	for path, versions := range excludes {
2227		for _, version := range versions {
2228			file.AddExclude(path, version)
2229		}
2230	}
2231	file.SortBlocks()
2232	return file, nil
2233}
2234
2235func buildWorkspaceSumFile(ctx context.Context, modFiles map[span.URI]struct{}, fs source.FileSource) ([]byte, error) {
2236	allSums := map[module.Version][]string{}
2237	for modURI := range modFiles {
2238		// TODO(rfindley): factor out this pattern into a uripath package.
2239		sumURI := span.URIFromPath(filepath.Join(filepath.Dir(modURI.Filename()), "go.sum"))
2240		fh, err := fs.GetFile(ctx, sumURI)
2241		if err != nil {
2242			continue
2243		}
2244		data, err := fh.Read()
2245		if os.IsNotExist(err) {
2246			continue
2247		}
2248		if err != nil {
2249			return nil, errors.Errorf("reading go sum: %w", err)
2250		}
2251		if err := readGoSum(allSums, sumURI.Filename(), data); err != nil {
2252			return nil, err
2253		}
2254	}
2255	// This logic to write go.sum is copied (with minor modifications) from
2256	// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/modfetch/fetch.go;l=631;drc=762eda346a9f4062feaa8a9fc0d17d72b11586f0
2257	var mods []module.Version
2258	for m := range allSums {
2259		mods = append(mods, m)
2260	}
2261	module.Sort(mods)
2262
2263	var buf bytes.Buffer
2264	for _, m := range mods {
2265		list := allSums[m]
2266		sort.Strings(list)
2267		// Note (rfindley): here we add all sum lines without verification, because
2268		// the assumption is that if they come from a go.sum file, they are
2269		// trusted.
2270		for _, h := range list {
2271			fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
2272		}
2273	}
2274	return buf.Bytes(), nil
2275}
2276
2277// readGoSum is copied (with minor modifications) from
2278// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/modfetch/fetch.go;l=398;drc=762eda346a9f4062feaa8a9fc0d17d72b11586f0
2279func readGoSum(dst map[module.Version][]string, file string, data []byte) error {
2280	lineno := 0
2281	for len(data) > 0 {
2282		var line []byte
2283		lineno++
2284		i := bytes.IndexByte(data, '\n')
2285		if i < 0 {
2286			line, data = data, nil
2287		} else {
2288			line, data = data[:i], data[i+1:]
2289		}
2290		f := strings.Fields(string(line))
2291		if len(f) == 0 {
2292			// blank line; skip it
2293			continue
2294		}
2295		if len(f) != 3 {
2296			return fmt.Errorf("malformed go.sum:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
2297		}
2298		mod := module.Version{Path: f[0], Version: f[1]}
2299		dst[mod] = append(dst[mod], f[2])
2300	}
2301	return nil
2302}
2303