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