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