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