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