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 / snapshot.go
1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package cache
6
7 import (
8         "bytes"
9         "context"
10         "fmt"
11         "go/ast"
12         "go/token"
13         "go/types"
14         "io"
15         "os"
16         "path/filepath"
17         "sort"
18         "strconv"
19         "strings"
20         "sync"
21
22         "golang.org/x/mod/modfile"
23         "golang.org/x/mod/module"
24         "golang.org/x/tools/go/analysis"
25         "golang.org/x/tools/go/packages"
26         "golang.org/x/tools/internal/event"
27         "golang.org/x/tools/internal/gocommand"
28         "golang.org/x/tools/internal/lsp/debug/tag"
29         "golang.org/x/tools/internal/lsp/source"
30         "golang.org/x/tools/internal/memoize"
31         "golang.org/x/tools/internal/packagesinternal"
32         "golang.org/x/tools/internal/span"
33         "golang.org/x/tools/internal/typesinternal"
34         errors "golang.org/x/xerrors"
35 )
36
37 type snapshot struct {
38         memoize.Arg // allow as a memoize.Function arg
39
40         id   uint64
41         view *View
42
43         // the cache generation that contains the data for this snapshot.
44         generation *memoize.Generation
45
46         // builtin pins the AST and package for builtin.go in memory.
47         builtin *builtinPackageHandle
48
49         // The snapshot's initialization state is controlled by the fields below.
50         //
51         // initializeOnce guards snapshot initialization. Each snapshot is
52         // initialized at most once: reinitialization is triggered on later snapshots
53         // by invalidating this field.
54         initializeOnce *sync.Once
55         // initializedErr holds the last error resulting from initialization. If
56         // initialization fails, we only retry when the the workspace modules change,
57         // to avoid too many go/packages calls.
58         initializedErr error
59
60         // mu guards all of the maps in the snapshot.
61         mu sync.Mutex
62
63         // ids maps file URIs to package IDs.
64         // It may be invalidated on calls to go/packages.
65         ids map[span.URI][]packageID
66
67         // metadata maps file IDs to their associated metadata.
68         // It may invalidated on calls to go/packages.
69         metadata map[packageID]*metadata
70
71         // importedBy maps package IDs to the list of packages that import them.
72         importedBy map[packageID][]packageID
73
74         // files maps file URIs to their corresponding FileHandles.
75         // It may invalidated when a file's content changes.
76         files map[span.URI]source.VersionedFileHandle
77
78         // goFiles maps a parseKey to its parseGoHandle.
79         goFiles map[parseKey]*parseGoHandle
80
81         // packages maps a packageKey to a set of packageHandles to which that file belongs.
82         // It may be invalidated when a file's content changes.
83         packages map[packageKey]*packageHandle
84
85         // actions maps an actionkey to its actionHandle.
86         actions map[actionKey]*actionHandle
87
88         // workspacePackages contains the workspace's packages, which are loaded
89         // when the view is created.
90         workspacePackages map[packageID]packagePath
91
92         // unloadableFiles keeps track of files that we've failed to load.
93         unloadableFiles map[span.URI]struct{}
94
95         // parseModHandles keeps track of any ParseModHandles for the snapshot.
96         // The handles need not refer to only the view's go.mod file.
97         parseModHandles map[span.URI]*parseModHandle
98
99         // Preserve go.mod-related handles to avoid garbage-collecting the results
100         // of various calls to the go command. The handles need not refer to only
101         // the view's go.mod file.
102         modTidyHandles    map[span.URI]*modTidyHandle
103         modUpgradeHandles map[span.URI]*modUpgradeHandle
104         modWhyHandles     map[span.URI]*modWhyHandle
105
106         workspace          *workspace
107         workspaceDirHandle *memoize.Handle
108 }
109
110 type packageKey struct {
111         mode source.ParseMode
112         id   packageID
113 }
114
115 type actionKey struct {
116         pkg      packageKey
117         analyzer *analysis.Analyzer
118 }
119
120 func (s *snapshot) ID() uint64 {
121         return s.id
122 }
123
124 func (s *snapshot) View() source.View {
125         return s.view
126 }
127
128 func (s *snapshot) FileSet() *token.FileSet {
129         return s.view.session.cache.fset
130 }
131
132 func (s *snapshot) ModFiles() []span.URI {
133         var uris []span.URI
134         for modURI := range s.workspace.activeModFiles() {
135                 uris = append(uris, modURI)
136         }
137         return uris
138 }
139
140 func (s *snapshot) ValidBuildConfiguration() bool {
141         return validBuildConfiguration(s.view.rootURI, &s.view.workspaceInformation, s.workspace.activeModFiles())
142 }
143
144 // workspaceMode describes the way in which the snapshot's workspace should
145 // be loaded.
146 func (s *snapshot) workspaceMode() workspaceMode {
147         var mode workspaceMode
148
149         // If the view has an invalid configuration, don't build the workspace
150         // module.
151         validBuildConfiguration := s.ValidBuildConfiguration()
152         if !validBuildConfiguration {
153                 return mode
154         }
155         // If the view is not in a module and contains no modules, but still has a
156         // valid workspace configuration, do not create the workspace module.
157         // It could be using GOPATH or a different build system entirely.
158         if len(s.workspace.activeModFiles()) == 0 && validBuildConfiguration {
159                 return mode
160         }
161         mode |= moduleMode
162         options := s.view.Options()
163         // The -modfile flag is available for Go versions >= 1.14.
164         if options.TempModfile && s.view.workspaceInformation.goversion >= 14 {
165                 mode |= tempModfile
166         }
167         // If the user is intentionally limiting their workspace scope, don't
168         // enable multi-module workspace mode.
169         // TODO(rstambler): This should only change the calculation of the root,
170         // not the mode.
171         if !options.ExpandWorkspaceToModule {
172                 return mode
173         }
174         // The workspace module has been disabled by the user.
175         if !options.ExperimentalWorkspaceModule {
176                 return mode
177         }
178         mode |= usesWorkspaceModule
179         return mode
180 }
181
182 // config returns the configuration used for the snapshot's interaction with
183 // the go/packages API. It uses the given working directory.
184 //
185 // TODO(rstambler): go/packages requires that we do not provide overlays for
186 // multiple modules in on config, so buildOverlay needs to filter overlays by
187 // module.
188 func (s *snapshot) config(ctx context.Context, inv *gocommand.Invocation) *packages.Config {
189         s.view.optionsMu.Lock()
190         verboseOutput := s.view.options.VerboseOutput
191         s.view.optionsMu.Unlock()
192
193         cfg := &packages.Config{
194                 Context:    ctx,
195                 Dir:        inv.WorkingDir,
196                 Env:        inv.Env,
197                 BuildFlags: inv.BuildFlags,
198                 Mode: packages.NeedName |
199                         packages.NeedFiles |
200                         packages.NeedCompiledGoFiles |
201                         packages.NeedImports |
202                         packages.NeedDeps |
203                         packages.NeedTypesSizes |
204                         packages.NeedModule,
205                 Fset:    s.view.session.cache.fset,
206                 Overlay: s.buildOverlay(),
207                 ParseFile: func(*token.FileSet, string, []byte) (*ast.File, error) {
208                         panic("go/packages must not be used to parse files")
209                 },
210                 Logf: func(format string, args ...interface{}) {
211                         if verboseOutput {
212                                 event.Log(ctx, fmt.Sprintf(format, args...))
213                         }
214                 },
215                 Tests: true,
216         }
217         packagesinternal.SetModFile(cfg, inv.ModFile)
218         packagesinternal.SetModFlag(cfg, inv.ModFlag)
219         // We want to type check cgo code if go/types supports it.
220         if typesinternal.SetUsesCgo(&types.Config{}) {
221                 cfg.Mode |= packages.LoadMode(packagesinternal.TypecheckCgo)
222         }
223         packagesinternal.SetGoCmdRunner(cfg, s.view.session.gocmdRunner)
224         return cfg
225 }
226
227 func (s *snapshot) RunGoCommandDirect(ctx context.Context, mode source.InvocationMode, inv *gocommand.Invocation) (*bytes.Buffer, error) {
228         _, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
229         if err != nil {
230                 return nil, err
231         }
232         defer cleanup()
233
234         return s.view.session.gocmdRunner.Run(ctx, *inv)
235 }
236
237 func (s *snapshot) RunGoCommandPiped(ctx context.Context, mode source.InvocationMode, inv *gocommand.Invocation, stdout, stderr io.Writer) error {
238         _, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
239         if err != nil {
240                 return err
241         }
242         defer cleanup()
243         return s.view.session.gocmdRunner.RunPiped(ctx, *inv, stdout, stderr)
244 }
245
246 func (s *snapshot) goCommandInvocation(ctx context.Context, mode source.InvocationMode, inv *gocommand.Invocation) (tmpURI span.URI, updatedInv *gocommand.Invocation, cleanup func(), err error) {
247         s.view.optionsMu.Lock()
248         inv.Env = append(append(append(os.Environ(), s.view.options.EnvSlice()...), inv.Env...), "GO111MODULE="+s.view.go111module)
249         inv.BuildFlags = append([]string{}, s.view.options.BuildFlags...)
250         s.view.optionsMu.Unlock()
251         cleanup = func() {} // fallback
252
253         var modURI span.URI
254         if s.workspaceMode()&moduleMode != 0 {
255                 // Select the module context to use.
256                 // If we're type checking, we need to use the workspace context, meaning
257                 // the main (workspace) module. Otherwise, we should use the module for
258                 // the passed-in working dir.
259                 if mode == source.ForTypeChecking {
260                         if s.workspaceMode()&usesWorkspaceModule == 0 {
261                                 for m := range s.workspace.activeModFiles() { // range to access the only element
262                                         modURI = m
263                                 }
264                         } else {
265                                 var tmpDir span.URI
266                                 var err error
267                                 tmpDir, err = s.getWorkspaceDir(ctx)
268                                 if err != nil {
269                                         return "", nil, cleanup, err
270                                 }
271                                 inv.WorkingDir = tmpDir.Filename()
272                                 modURI = span.URIFromPath(filepath.Join(tmpDir.Filename(), "go.mod"))
273                         }
274                 } else {
275                         modURI = s.GoModForFile(ctx, span.URIFromPath(inv.WorkingDir))
276                 }
277         }
278
279         wantTempMod := mode != source.UpdateUserModFile
280         needTempMod := mode == source.WriteTemporaryModFile
281         tempMod := wantTempMod && s.workspaceMode()&tempModfile != 0
282         if needTempMod && !tempMod {
283                 return "", nil, cleanup, source.ErrTmpModfileUnsupported
284         }
285
286         if tempMod {
287                 if modURI == "" {
288                         return "", nil, cleanup, fmt.Errorf("no go.mod file found in %s", inv.WorkingDir)
289                 }
290                 modFH, err := s.GetFile(ctx, modURI)
291                 if err != nil {
292                         return "", nil, cleanup, err
293                 }
294                 // Use the go.sum if it happens to be available.
295                 gosum := s.goSum(ctx, modURI)
296                 tmpURI, cleanup, err = tempModFile(modFH, gosum)
297                 if err != nil {
298                         return "", nil, cleanup, err
299                 }
300                 inv.ModFile = tmpURI.Filename()
301         }
302
303         var modContent []byte
304         if modURI != "" {
305                 modFH, err := s.GetFile(ctx, modURI)
306                 if err != nil {
307                         return "", nil, cleanup, err
308                 }
309                 modContent, err = modFH.Read()
310                 if err != nil {
311                         return "", nil, nil, err
312                 }
313         }
314         modMod, err := s.needsModEqualsMod(ctx, modURI, modContent)
315         if err != nil {
316                 return "", nil, cleanup, err
317         }
318         if modMod {
319                 inv.ModFlag = "mod"
320         }
321
322         return tmpURI, inv, cleanup, nil
323 }
324
325 func (s *snapshot) buildOverlay() map[string][]byte {
326         s.mu.Lock()
327         defer s.mu.Unlock()
328
329         overlays := make(map[string][]byte)
330         for uri, fh := range s.files {
331                 overlay, ok := fh.(*overlay)
332                 if !ok {
333                         continue
334                 }
335                 if overlay.saved {
336                         continue
337                 }
338                 // TODO(rstambler): Make sure not to send overlays outside of the current view.
339                 overlays[uri.Filename()] = overlay.text
340         }
341         return overlays
342 }
343
344 func hashUnsavedOverlays(files map[span.URI]source.VersionedFileHandle) string {
345         var unsaved []string
346         for uri, fh := range files {
347                 if overlay, ok := fh.(*overlay); ok && !overlay.saved {
348                         unsaved = append(unsaved, uri.Filename())
349                 }
350         }
351         sort.Strings(unsaved)
352         return hashContents([]byte(strings.Join(unsaved, "")))
353 }
354
355 func (s *snapshot) PackagesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode) ([]source.Package, error) {
356         ctx = event.Label(ctx, tag.URI.Of(uri))
357
358         phs, err := s.packageHandlesForFile(ctx, uri, mode)
359         if err != nil {
360                 return nil, err
361         }
362         var pkgs []source.Package
363         for _, ph := range phs {
364                 pkg, err := ph.check(ctx, s)
365                 if err != nil {
366                         return nil, err
367                 }
368                 pkgs = append(pkgs, pkg)
369         }
370         return pkgs, nil
371 }
372
373 func (s *snapshot) PackageForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode, pkgPolicy source.PackageFilter) (source.Package, error) {
374         ctx = event.Label(ctx, tag.URI.Of(uri))
375
376         phs, err := s.packageHandlesForFile(ctx, uri, mode)
377         if err != nil {
378                 return nil, err
379         }
380
381         if len(phs) < 1 {
382                 return nil, errors.Errorf("no packages")
383         }
384
385         ph := phs[0]
386         for _, handle := range phs[1:] {
387                 switch pkgPolicy {
388                 case source.WidestPackage:
389                         if ph == nil || len(handle.CompiledGoFiles()) > len(ph.CompiledGoFiles()) {
390                                 ph = handle
391                         }
392                 case source.NarrowestPackage:
393                         if ph == nil || len(handle.CompiledGoFiles()) < len(ph.CompiledGoFiles()) {
394                                 ph = handle
395                         }
396                 }
397         }
398         if ph == nil {
399                 return nil, errors.Errorf("no packages in input")
400         }
401
402         return ph.check(ctx, s)
403 }
404
405 func (s *snapshot) packageHandlesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode) ([]*packageHandle, error) {
406         // Check if we should reload metadata for the file. We don't invalidate IDs
407         // (though we should), so the IDs will be a better source of truth than the
408         // metadata. If there are no IDs for the file, then we should also reload.
409         fh, err := s.GetFile(ctx, uri)
410         if err != nil {
411                 return nil, err
412         }
413         if fh.Kind() != source.Go {
414                 return nil, fmt.Errorf("no packages for non-Go file %s", uri)
415         }
416         ids := s.getIDsForURI(uri)
417         reload := len(ids) == 0
418         for _, id := range ids {
419                 // Reload package metadata if any of the metadata has missing
420                 // dependencies, in case something has changed since the last time we
421                 // reloaded it.
422                 if m := s.getMetadata(id); m == nil {
423                         reload = true
424                         break
425                 }
426                 // TODO(golang/go#36918): Previously, we would reload any package with
427                 // missing dependencies. This is expensive and results in too many
428                 // calls to packages.Load. Determine what we should do instead.
429         }
430         if reload {
431                 if err := s.load(ctx, fileURI(uri)); err != nil {
432                         return nil, err
433                 }
434         }
435         // Get the list of IDs from the snapshot again, in case it has changed.
436         var phs []*packageHandle
437         for _, id := range s.getIDsForURI(uri) {
438                 var parseModes []source.ParseMode
439                 switch mode {
440                 case source.TypecheckAll:
441                         if s.workspaceParseMode(id) == source.ParseFull {
442                                 parseModes = []source.ParseMode{source.ParseFull}
443                         } else {
444                                 parseModes = []source.ParseMode{source.ParseExported, source.ParseFull}
445                         }
446                 case source.TypecheckFull:
447                         parseModes = []source.ParseMode{source.ParseFull}
448                 case source.TypecheckWorkspace:
449                         parseModes = []source.ParseMode{s.workspaceParseMode(id)}
450                 }
451
452                 for _, parseMode := range parseModes {
453                         ph, err := s.buildPackageHandle(ctx, id, parseMode)
454                         if err != nil {
455                                 return nil, err
456                         }
457                         phs = append(phs, ph)
458                 }
459         }
460
461         return phs, nil
462 }
463
464 func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]source.Package, error) {
465         if err := s.awaitLoaded(ctx); err != nil {
466                 return nil, err
467         }
468         ids := make(map[packageID]struct{})
469         s.transitiveReverseDependencies(packageID(id), ids)
470
471         // Make sure to delete the original package ID from the map.
472         delete(ids, packageID(id))
473
474         var pkgs []source.Package
475         for id := range ids {
476                 pkg, err := s.checkedPackage(ctx, id, s.workspaceParseMode(id))
477                 if err != nil {
478                         return nil, err
479                 }
480                 pkgs = append(pkgs, pkg)
481         }
482         return pkgs, nil
483 }
484
485 func (s *snapshot) checkedPackage(ctx context.Context, id packageID, mode source.ParseMode) (*pkg, error) {
486         ph, err := s.buildPackageHandle(ctx, id, mode)
487         if err != nil {
488                 return nil, err
489         }
490         return ph.check(ctx, s)
491 }
492
493 // transitiveReverseDependencies populates the uris map with file URIs
494 // belonging to the provided package and its transitive reverse dependencies.
495 func (s *snapshot) transitiveReverseDependencies(id packageID, ids map[packageID]struct{}) {
496         if _, ok := ids[id]; ok {
497                 return
498         }
499         if s.getMetadata(id) == nil {
500                 return
501         }
502         ids[id] = struct{}{}
503         importedBy := s.getImportedBy(id)
504         for _, parentID := range importedBy {
505                 s.transitiveReverseDependencies(parentID, ids)
506         }
507 }
508
509 func (s *snapshot) getGoFile(key parseKey) *parseGoHandle {
510         s.mu.Lock()
511         defer s.mu.Unlock()
512         return s.goFiles[key]
513 }
514
515 func (s *snapshot) addGoFile(key parseKey, pgh *parseGoHandle) *parseGoHandle {
516         s.mu.Lock()
517         defer s.mu.Unlock()
518         if existing, ok := s.goFiles[key]; ok {
519                 return existing
520         }
521         s.goFiles[key] = pgh
522         return pgh
523 }
524
525 func (s *snapshot) getParseModHandle(uri span.URI) *parseModHandle {
526         s.mu.Lock()
527         defer s.mu.Unlock()
528         return s.parseModHandles[uri]
529 }
530
531 func (s *snapshot) getModWhyHandle(uri span.URI) *modWhyHandle {
532         s.mu.Lock()
533         defer s.mu.Unlock()
534         return s.modWhyHandles[uri]
535 }
536
537 func (s *snapshot) getModUpgradeHandle(uri span.URI) *modUpgradeHandle {
538         s.mu.Lock()
539         defer s.mu.Unlock()
540         return s.modUpgradeHandles[uri]
541 }
542
543 func (s *snapshot) getModTidyHandle(uri span.URI) *modTidyHandle {
544         s.mu.Lock()
545         defer s.mu.Unlock()
546         return s.modTidyHandles[uri]
547 }
548
549 func (s *snapshot) getImportedBy(id packageID) []packageID {
550         s.mu.Lock()
551         defer s.mu.Unlock()
552         return s.getImportedByLocked(id)
553 }
554
555 func (s *snapshot) getImportedByLocked(id packageID) []packageID {
556         // If we haven't rebuilt the import graph since creating the snapshot.
557         if len(s.importedBy) == 0 {
558                 s.rebuildImportGraph()
559         }
560         return s.importedBy[id]
561 }
562
563 func (s *snapshot) clearAndRebuildImportGraph() {
564         s.mu.Lock()
565         defer s.mu.Unlock()
566
567         // Completely invalidate the original map.
568         s.importedBy = make(map[packageID][]packageID)
569         s.rebuildImportGraph()
570 }
571
572 func (s *snapshot) rebuildImportGraph() {
573         for id, m := range s.metadata {
574                 for _, importID := range m.deps {
575                         s.importedBy[importID] = append(s.importedBy[importID], id)
576                 }
577         }
578 }
579
580 func (s *snapshot) addPackageHandle(ph *packageHandle) *packageHandle {
581         s.mu.Lock()
582         defer s.mu.Unlock()
583
584         // If the package handle has already been cached,
585         // return the cached handle instead of overriding it.
586         if ph, ok := s.packages[ph.packageKey()]; ok {
587                 return ph
588         }
589         s.packages[ph.packageKey()] = ph
590         return ph
591 }
592
593 func (s *snapshot) workspacePackageIDs() (ids []packageID) {
594         s.mu.Lock()
595         defer s.mu.Unlock()
596
597         for id := range s.workspacePackages {
598                 ids = append(ids, id)
599         }
600         return ids
601 }
602
603 func (s *snapshot) WorkspaceDirectories(ctx context.Context) []span.URI {
604         return s.workspace.dirs(ctx, s)
605 }
606
607 func (s *snapshot) WorkspacePackages(ctx context.Context) ([]source.Package, error) {
608         if err := s.awaitLoaded(ctx); err != nil {
609                 return nil, err
610         }
611         var pkgs []source.Package
612         for _, pkgID := range s.workspacePackageIDs() {
613                 pkg, err := s.checkedPackage(ctx, pkgID, s.workspaceParseMode(pkgID))
614                 if err != nil {
615                         return nil, err
616                 }
617                 pkgs = append(pkgs, pkg)
618         }
619         return pkgs, nil
620 }
621
622 func (s *snapshot) KnownPackages(ctx context.Context) ([]source.Package, error) {
623         if err := s.awaitLoaded(ctx); err != nil {
624                 return nil, err
625         }
626
627         // The WorkspaceSymbols implementation relies on this function returning
628         // workspace packages first.
629         ids := s.workspacePackageIDs()
630         s.mu.Lock()
631         for id := range s.metadata {
632                 if _, ok := s.workspacePackages[id]; ok {
633                         continue
634                 }
635                 ids = append(ids, id)
636         }
637         s.mu.Unlock()
638
639         var pkgs []source.Package
640         for _, id := range ids {
641                 pkg, err := s.checkedPackage(ctx, id, s.workspaceParseMode(id))
642                 if err != nil {
643                         return nil, err
644                 }
645                 pkgs = append(pkgs, pkg)
646         }
647         return pkgs, nil
648 }
649
650 func (s *snapshot) CachedImportPaths(ctx context.Context) (map[string]source.Package, error) {
651         // Don't reload workspace package metadata.
652         // This function is meant to only return currently cached information.
653         s.AwaitInitialized(ctx)
654
655         s.mu.Lock()
656         defer s.mu.Unlock()
657
658         results := map[string]source.Package{}
659         for _, ph := range s.packages {
660                 cachedPkg, err := ph.cached(s.generation)
661                 if err != nil {
662                         continue
663                 }
664                 for importPath, newPkg := range cachedPkg.imports {
665                         if oldPkg, ok := results[string(importPath)]; ok {
666                                 // Using the same trick as NarrowestPackage, prefer non-variants.
667                                 if len(newPkg.compiledGoFiles) < len(oldPkg.(*pkg).compiledGoFiles) {
668                                         results[string(importPath)] = newPkg
669                                 }
670                         } else {
671                                 results[string(importPath)] = newPkg
672                         }
673                 }
674         }
675         return results, nil
676 }
677
678 func (s *snapshot) GoModForFile(ctx context.Context, uri span.URI) span.URI {
679         var match span.URI
680         for modURI := range s.workspace.activeModFiles() {
681                 if !source.InDir(dirURI(modURI).Filename(), uri.Filename()) {
682                         continue
683                 }
684                 if len(modURI) > len(match) {
685                         match = modURI
686                 }
687         }
688         return match
689 }
690
691 func (s *snapshot) getPackage(id packageID, mode source.ParseMode) *packageHandle {
692         s.mu.Lock()
693         defer s.mu.Unlock()
694
695         key := packageKey{
696                 id:   id,
697                 mode: mode,
698         }
699         return s.packages[key]
700 }
701
702 func (s *snapshot) getActionHandle(id packageID, m source.ParseMode, a *analysis.Analyzer) *actionHandle {
703         s.mu.Lock()
704         defer s.mu.Unlock()
705
706         key := actionKey{
707                 pkg: packageKey{
708                         id:   id,
709                         mode: m,
710                 },
711                 analyzer: a,
712         }
713         return s.actions[key]
714 }
715
716 func (s *snapshot) addActionHandle(ah *actionHandle) *actionHandle {
717         s.mu.Lock()
718         defer s.mu.Unlock()
719
720         key := actionKey{
721                 analyzer: ah.analyzer,
722                 pkg: packageKey{
723                         id:   ah.pkg.m.id,
724                         mode: ah.pkg.mode,
725                 },
726         }
727         if ah, ok := s.actions[key]; ok {
728                 return ah
729         }
730         s.actions[key] = ah
731         return ah
732 }
733
734 func (s *snapshot) getIDsForURI(uri span.URI) []packageID {
735         s.mu.Lock()
736         defer s.mu.Unlock()
737
738         return s.ids[uri]
739 }
740
741 func (s *snapshot) getMetadataForURILocked(uri span.URI) (metadata []*metadata) {
742         // TODO(matloob): uri can be a file or directory. Should we update the mappings
743         // to map directories to their contained packages?
744
745         for _, id := range s.ids[uri] {
746                 if m, ok := s.metadata[id]; ok {
747                         metadata = append(metadata, m)
748                 }
749         }
750         return metadata
751 }
752
753 func (s *snapshot) getMetadata(id packageID) *metadata {
754         s.mu.Lock()
755         defer s.mu.Unlock()
756
757         return s.metadata[id]
758 }
759
760 func (s *snapshot) addID(uri span.URI, id packageID) {
761         s.mu.Lock()
762         defer s.mu.Unlock()
763
764         for i, existingID := range s.ids[uri] {
765                 // TODO: We should make sure not to set duplicate IDs,
766                 // and instead panic here. This can be done by making sure not to
767                 // reset metadata information for packages we've already seen.
768                 if existingID == id {
769                         return
770                 }
771                 // If we are setting a real ID, when the package had only previously
772                 // had a command-line-arguments ID, we should just replace it.
773                 if existingID == "command-line-arguments" {
774                         s.ids[uri][i] = id
775                         // Delete command-line-arguments if it was a workspace package.
776                         delete(s.workspacePackages, existingID)
777                         return
778                 }
779         }
780         s.ids[uri] = append(s.ids[uri], id)
781 }
782
783 func (s *snapshot) isWorkspacePackage(id packageID) (packagePath, bool) {
784         s.mu.Lock()
785         defer s.mu.Unlock()
786
787         scope, ok := s.workspacePackages[id]
788         return scope, ok
789 }
790
791 func (s *snapshot) FindFile(uri span.URI) source.VersionedFileHandle {
792         f, err := s.view.getFile(uri)
793         if err != nil {
794                 return nil
795         }
796
797         s.mu.Lock()
798         defer s.mu.Unlock()
799
800         return s.files[f.URI()]
801 }
802
803 // GetVersionedFile returns a File for the given URI. If the file is unknown it
804 // is added to the managed set.
805 //
806 // GetVersionedFile succeeds even if the file does not exist. A non-nil error return
807 // indicates some type of internal error, for example if ctx is cancelled.
808 func (s *snapshot) GetVersionedFile(ctx context.Context, uri span.URI) (source.VersionedFileHandle, error) {
809         f, err := s.view.getFile(uri)
810         if err != nil {
811                 return nil, err
812         }
813
814         s.mu.Lock()
815         defer s.mu.Unlock()
816         return s.getFileLocked(ctx, f)
817 }
818
819 // GetFile implements the fileSource interface by wrapping GetVersionedFile.
820 func (s *snapshot) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
821         return s.GetVersionedFile(ctx, uri)
822 }
823
824 func (s *snapshot) getFileLocked(ctx context.Context, f *fileBase) (source.VersionedFileHandle, error) {
825         if fh, ok := s.files[f.URI()]; ok {
826                 return fh, nil
827         }
828
829         fh, err := s.view.session.cache.getFile(ctx, f.URI())
830         if err != nil {
831                 return nil, err
832         }
833         closed := &closedFile{fh}
834         s.files[f.URI()] = closed
835         return closed, nil
836 }
837
838 func (s *snapshot) IsOpen(uri span.URI) bool {
839         s.mu.Lock()
840         defer s.mu.Unlock()
841
842         _, open := s.files[uri].(*overlay)
843         return open
844 }
845
846 func (s *snapshot) awaitLoaded(ctx context.Context) error {
847         // Do not return results until the snapshot's view has been initialized.
848         s.AwaitInitialized(ctx)
849
850         if err := s.reloadWorkspace(ctx); err != nil {
851                 return err
852         }
853         if err := s.reloadOrphanedFiles(ctx); err != nil {
854                 return err
855         }
856         // If we still have absolutely no metadata, check if the view failed to
857         // initialize and return any errors.
858         // TODO(rstambler): Should we clear the error after we return it?
859         s.mu.Lock()
860         defer s.mu.Unlock()
861         if len(s.metadata) == 0 {
862                 return s.initializedErr
863         }
864         return nil
865 }
866
867 func (s *snapshot) AwaitInitialized(ctx context.Context) {
868         select {
869         case <-ctx.Done():
870                 return
871         case <-s.view.initialWorkspaceLoad:
872         }
873         // We typically prefer to run something as intensive as the IWL without
874         // blocking. I'm not sure if there is a way to do that here.
875         s.initialize(ctx, false)
876 }
877
878 // reloadWorkspace reloads the metadata for all invalidated workspace packages.
879 func (s *snapshot) reloadWorkspace(ctx context.Context) error {
880         // If the view's build configuration is invalid, we cannot reload by
881         // package path. Just reload the directory instead.
882         if !s.ValidBuildConfiguration() {
883                 return s.load(ctx, viewLoadScope("LOAD_INVALID_VIEW"))
884         }
885
886         // See which of the workspace packages are missing metadata.
887         s.mu.Lock()
888         pkgPathSet := map[packagePath]struct{}{}
889         for id, pkgPath := range s.workspacePackages {
890                 // Don't try to reload "command-line-arguments" directly.
891                 if pkgPath == "command-line-arguments" {
892                         continue
893                 }
894                 if s.metadata[id] == nil {
895                         pkgPathSet[pkgPath] = struct{}{}
896                 }
897         }
898         s.mu.Unlock()
899
900         if len(pkgPathSet) == 0 {
901                 return nil
902         }
903         var pkgPaths []interface{}
904         for pkgPath := range pkgPathSet {
905                 pkgPaths = append(pkgPaths, pkgPath)
906         }
907         return s.load(ctx, pkgPaths...)
908 }
909
910 func (s *snapshot) reloadOrphanedFiles(ctx context.Context) error {
911         // When we load ./... or a package path directly, we may not get packages
912         // that exist only in overlays. As a workaround, we search all of the files
913         // available in the snapshot and reload their metadata individually using a
914         // file= query if the metadata is unavailable.
915         scopes := s.orphanedFileScopes()
916         if len(scopes) == 0 {
917                 return nil
918         }
919
920         err := s.load(ctx, scopes...)
921
922         // If we failed to load some files, i.e. they have no metadata,
923         // mark the failures so we don't bother retrying until the file's
924         // content changes.
925         //
926         // TODO(rstambler): This may be an overestimate if the load stopped
927         // early for an unrelated errors. Add a fallback?
928         //
929         // Check for context cancellation so that we don't incorrectly mark files
930         // as unloadable, but don't return before setting all workspace packages.
931         if ctx.Err() == nil && err != nil {
932                 event.Error(ctx, "reloadOrphanedFiles: failed to load", err, tag.Query.Of(scopes))
933                 s.mu.Lock()
934                 for _, scope := range scopes {
935                         uri := span.URI(scope.(fileURI))
936                         if s.getMetadataForURILocked(uri) == nil {
937                                 s.unloadableFiles[uri] = struct{}{}
938                         }
939                 }
940                 s.mu.Unlock()
941         }
942         return nil
943 }
944
945 func (s *snapshot) orphanedFileScopes() []interface{} {
946         s.mu.Lock()
947         defer s.mu.Unlock()
948
949         scopeSet := make(map[span.URI]struct{})
950         for uri, fh := range s.files {
951                 // Don't try to reload metadata for go.mod files.
952                 if fh.Kind() != source.Go {
953                         continue
954                 }
955                 // If the URI doesn't belong to this view, then it's not in a workspace
956                 // package and should not be reloaded directly.
957                 if !contains(s.view.session.viewsOf(uri), s.view) {
958                         continue
959                 }
960                 // If the file is not open and is in a vendor directory, don't treat it
961                 // like a workspace package.
962                 if _, ok := fh.(*overlay); !ok && inVendor(uri) {
963                         continue
964                 }
965                 // Don't reload metadata for files we've already deemed unloadable.
966                 if _, ok := s.unloadableFiles[uri]; ok {
967                         continue
968                 }
969                 if s.getMetadataForURILocked(uri) == nil {
970                         scopeSet[uri] = struct{}{}
971                 }
972         }
973         var scopes []interface{}
974         for uri := range scopeSet {
975                 scopes = append(scopes, fileURI(uri))
976         }
977         return scopes
978 }
979
980 func contains(views []*View, view *View) bool {
981         for _, v := range views {
982                 if v == view {
983                         return true
984                 }
985         }
986         return false
987 }
988
989 func inVendor(uri span.URI) bool {
990         toSlash := filepath.ToSlash(uri.Filename())
991         if !strings.Contains(toSlash, "/vendor/") {
992                 return false
993         }
994         // Only packages in _subdirectories_ of /vendor/ are considered vendored
995         // (/vendor/a/foo.go is vendored, /vendor/foo.go is not).
996         split := strings.Split(toSlash, "/vendor/")
997         if len(split) < 2 {
998                 return false
999         }
1000         return strings.Contains(split[1], "/")
1001 }
1002
1003 func generationName(v *View, snapshotID uint64) string {
1004         return fmt.Sprintf("v%v/%v", v.id, snapshotID)
1005 }
1006
1007 func (s *snapshot) clone(ctx context.Context, changes map[span.URI]*fileChange, forceReloadMetadata bool) (*snapshot, bool) {
1008         // Track some important types of changes.
1009         var (
1010                 vendorChanged  bool
1011                 modulesChanged bool
1012         )
1013         newWorkspace, workspaceChanged := s.workspace.invalidate(ctx, changes)
1014
1015         s.mu.Lock()
1016         defer s.mu.Unlock()
1017
1018         newGen := s.view.session.cache.store.Generation(generationName(s.view, s.id+1))
1019         result := &snapshot{
1020                 id:                s.id + 1,
1021                 generation:        newGen,
1022                 view:              s.view,
1023                 builtin:           s.builtin,
1024                 initializeOnce:    s.initializeOnce,
1025                 initializedErr:    s.initializedErr,
1026                 ids:               make(map[span.URI][]packageID),
1027                 importedBy:        make(map[packageID][]packageID),
1028                 metadata:          make(map[packageID]*metadata),
1029                 packages:          make(map[packageKey]*packageHandle),
1030                 actions:           make(map[actionKey]*actionHandle),
1031                 files:             make(map[span.URI]source.VersionedFileHandle),
1032                 goFiles:           make(map[parseKey]*parseGoHandle),
1033                 workspacePackages: make(map[packageID]packagePath),
1034                 unloadableFiles:   make(map[span.URI]struct{}),
1035                 parseModHandles:   make(map[span.URI]*parseModHandle),
1036                 modTidyHandles:    make(map[span.URI]*modTidyHandle),
1037                 modUpgradeHandles: make(map[span.URI]*modUpgradeHandle),
1038                 modWhyHandles:     make(map[span.URI]*modWhyHandle),
1039                 workspace:         newWorkspace,
1040         }
1041
1042         if !workspaceChanged && s.workspaceDirHandle != nil {
1043                 result.workspaceDirHandle = s.workspaceDirHandle
1044                 newGen.Inherit(s.workspaceDirHandle)
1045         }
1046
1047         if s.builtin != nil {
1048                 newGen.Inherit(s.builtin.handle)
1049         }
1050
1051         // Copy all of the FileHandles.
1052         for k, v := range s.files {
1053                 result.files[k] = v
1054         }
1055
1056         // Copy the set of unloadable files.
1057         for k, v := range s.unloadableFiles {
1058                 result.unloadableFiles[k] = v
1059         }
1060         // Copy all of the modHandles.
1061         for k, v := range s.parseModHandles {
1062                 result.parseModHandles[k] = v
1063         }
1064
1065         for k, v := range s.goFiles {
1066                 if _, ok := changes[k.file.URI]; ok {
1067                         continue
1068                 }
1069                 newGen.Inherit(v.handle)
1070                 newGen.Inherit(v.astCacheHandle)
1071                 result.goFiles[k] = v
1072         }
1073
1074         // Copy all of the go.mod-related handles. They may be invalidated later,
1075         // so we inherit them at the end of the function.
1076         for k, v := range s.modTidyHandles {
1077                 if _, ok := changes[k]; ok {
1078                         continue
1079                 }
1080                 result.modTidyHandles[k] = v
1081         }
1082         for k, v := range s.modUpgradeHandles {
1083                 if _, ok := changes[k]; ok {
1084                         continue
1085                 }
1086                 result.modUpgradeHandles[k] = v
1087         }
1088         for k, v := range s.modWhyHandles {
1089                 if _, ok := changes[k]; ok {
1090                         continue
1091                 }
1092                 result.modWhyHandles[k] = v
1093         }
1094
1095         // directIDs keeps track of package IDs that have directly changed.
1096         // It maps id->invalidateMetadata.
1097         directIDs := map[packageID]bool{}
1098         // Invalidate all package metadata if the workspace module has changed.
1099         if workspaceChanged {
1100                 for k := range s.metadata {
1101                         directIDs[k] = true
1102                 }
1103         }
1104
1105         for uri, change := range changes {
1106                 // Maybe reinitialize the view if we see a change in the vendor
1107                 // directory.
1108                 if inVendor(uri) {
1109                         vendorChanged = true
1110                 }
1111
1112                 // The original FileHandle for this URI is cached on the snapshot.
1113                 originalFH := s.files[uri]
1114
1115                 // Check if the file's package name or imports have changed,
1116                 // and if so, invalidate this file's packages' metadata.
1117                 invalidateMetadata := forceReloadMetadata || s.shouldInvalidateMetadata(ctx, result, originalFH, change.fileHandle)
1118
1119                 // Mark all of the package IDs containing the given file.
1120                 // TODO: if the file has moved into a new package, we should invalidate that too.
1121                 filePackages := guessPackagesForURI(uri, s.ids)
1122                 for _, id := range filePackages {
1123                         directIDs[id] = directIDs[id] || invalidateMetadata
1124                 }
1125
1126                 // Invalidate the previous modTidyHandle if any of the files have been
1127                 // saved or if any of the metadata has been invalidated.
1128                 if invalidateMetadata || fileWasSaved(originalFH, change.fileHandle) {
1129                         // TODO(rstambler): Only delete mod handles for which the
1130                         // withoutURI is relevant.
1131                         for k := range s.modTidyHandles {
1132                                 delete(result.modTidyHandles, k)
1133                         }
1134                         for k := range s.modUpgradeHandles {
1135                                 delete(result.modUpgradeHandles, k)
1136                         }
1137                         for k := range s.modWhyHandles {
1138                                 delete(result.modWhyHandles, k)
1139                         }
1140                 }
1141                 if isGoMod(uri) {
1142                         // If the view's go.mod file's contents have changed, invalidate
1143                         // the metadata for every known package in the snapshot.
1144                         delete(result.parseModHandles, uri)
1145                         if _, ok := result.workspace.activeModFiles()[uri]; ok {
1146                                 modulesChanged = true
1147                         }
1148                 }
1149                 // Handle the invalidated file; it may have new contents or not exist.
1150                 if !change.exists {
1151                         delete(result.files, uri)
1152                 } else {
1153                         result.files[uri] = change.fileHandle
1154                 }
1155                 // Make sure to remove the changed file from the unloadable set.
1156                 delete(result.unloadableFiles, uri)
1157         }
1158
1159         // Invalidate reverse dependencies too.
1160         // TODO(heschi): figure out the locking model and use transitiveReverseDeps?
1161         // transitiveIDs keeps track of transitive reverse dependencies.
1162         // If an ID is present in the map, invalidate its types.
1163         // If an ID's value is true, invalidate its metadata too.
1164         transitiveIDs := make(map[packageID]bool)
1165         var addRevDeps func(packageID, bool)
1166         addRevDeps = func(id packageID, invalidateMetadata bool) {
1167                 current, seen := transitiveIDs[id]
1168                 newInvalidateMetadata := current || invalidateMetadata
1169
1170                 // If we've already seen this ID, and the value of invalidate
1171                 // metadata has not changed, we can return early.
1172                 if seen && current == newInvalidateMetadata {
1173                         return
1174                 }
1175                 transitiveIDs[id] = newInvalidateMetadata
1176                 for _, rid := range s.getImportedByLocked(id) {
1177                         addRevDeps(rid, invalidateMetadata)
1178                 }
1179         }
1180         for id, invalidateMetadata := range directIDs {
1181                 addRevDeps(id, invalidateMetadata)
1182         }
1183
1184         // Copy the package type information.
1185         for k, v := range s.packages {
1186                 if _, ok := transitiveIDs[k.id]; ok {
1187                         continue
1188                 }
1189                 newGen.Inherit(v.handle)
1190                 result.packages[k] = v
1191         }
1192         // Copy the package analysis information.
1193         for k, v := range s.actions {
1194                 if _, ok := transitiveIDs[k.pkg.id]; ok {
1195                         continue
1196                 }
1197                 newGen.Inherit(v.handle)
1198                 result.actions[k] = v
1199         }
1200         // Copy the package metadata. We only need to invalidate packages directly
1201         // containing the affected file, and only if it changed in a relevant way.
1202         for k, v := range s.metadata {
1203                 if invalidateMetadata, ok := transitiveIDs[k]; invalidateMetadata && ok {
1204                         continue
1205                 }
1206                 result.metadata[k] = v
1207         }
1208         // Copy the URI to package ID mappings, skipping only those URIs whose
1209         // metadata will be reloaded in future calls to load.
1210 copyIDs:
1211         for k, ids := range s.ids {
1212                 for _, id := range ids {
1213                         if invalidateMetadata, ok := transitiveIDs[id]; invalidateMetadata && ok {
1214                                 continue copyIDs
1215                         }
1216                 }
1217                 result.ids[k] = ids
1218         }
1219         // Copy the set of initally loaded packages.
1220         for id, pkgPath := range s.workspacePackages {
1221                 // Packages with the id "command-line-arguments" are generated by the
1222                 // go command when the user is outside of GOPATH and outside of a
1223                 // module. Do not cache them as workspace packages for longer than
1224                 // necessary.
1225                 if id == "command-line-arguments" {
1226                         if invalidateMetadata, ok := transitiveIDs[id]; invalidateMetadata && ok {
1227                                 continue
1228                         }
1229                 }
1230
1231                 // If all the files we know about in a package have been deleted,
1232                 // the package is gone and we should no longer try to load it.
1233                 if m := s.metadata[id]; m != nil {
1234                         hasFiles := false
1235                         for _, uri := range s.metadata[id].goFiles {
1236                                 if _, ok := result.files[uri]; ok {
1237                                         hasFiles = true
1238                                         break
1239                                 }
1240                         }
1241                         if !hasFiles {
1242                                 continue
1243                         }
1244                 }
1245
1246                 result.workspacePackages[id] = pkgPath
1247         }
1248
1249         // Inherit all of the go.mod-related handles.
1250         for _, v := range result.modTidyHandles {
1251                 newGen.Inherit(v.handle)
1252         }
1253         for _, v := range result.modUpgradeHandles {
1254                 newGen.Inherit(v.handle)
1255         }
1256         for _, v := range result.modWhyHandles {
1257                 newGen.Inherit(v.handle)
1258         }
1259         for _, v := range result.parseModHandles {
1260                 newGen.Inherit(v.handle)
1261         }
1262         // Don't bother copying the importedBy graph,
1263         // as it changes each time we update metadata.
1264
1265         // If the snapshot's workspace mode has changed, the packages loaded using
1266         // the previous mode are no longer relevant, so clear them out.
1267         if s.workspaceMode() != result.workspaceMode() {
1268                 result.workspacePackages = map[packageID]packagePath{}
1269         }
1270
1271         // The snapshot may need to be reinitialized.
1272         if modulesChanged || workspaceChanged || vendorChanged {
1273                 if workspaceChanged || result.initializedErr != nil {
1274                         result.initializeOnce = &sync.Once{}
1275                 }
1276         }
1277         return result, workspaceChanged
1278 }
1279
1280 // guessPackagesForURI returns all packages related to uri. If we haven't seen this
1281 // URI before, we guess based on files in the same directory. This is of course
1282 // incorrect in build systems where packages are not organized by directory.
1283 func guessPackagesForURI(uri span.URI, known map[span.URI][]packageID) []packageID {
1284         packages := known[uri]
1285         if len(packages) > 0 {
1286                 // We've seen this file before.
1287                 return packages
1288         }
1289         // This is a file we don't yet know about. Guess relevant packages by
1290         // considering files in the same directory.
1291
1292         // Cache of FileInfo to avoid unnecessary stats for multiple files in the
1293         // same directory.
1294         stats := make(map[string]struct {
1295                 os.FileInfo
1296                 error
1297         })
1298         getInfo := func(dir string) (os.FileInfo, error) {
1299                 if res, ok := stats[dir]; ok {
1300                         return res.FileInfo, res.error
1301                 }
1302                 fi, err := os.Stat(dir)
1303                 stats[dir] = struct {
1304                         os.FileInfo
1305                         error
1306                 }{fi, err}
1307                 return fi, err
1308         }
1309         dir := filepath.Dir(uri.Filename())
1310         fi, err := getInfo(dir)
1311         if err != nil {
1312                 return nil
1313         }
1314
1315         // Aggregate all possibly relevant package IDs.
1316         var found []packageID
1317         for knownURI, ids := range known {
1318                 knownDir := filepath.Dir(knownURI.Filename())
1319                 knownFI, err := getInfo(knownDir)
1320                 if err != nil {
1321                         continue
1322                 }
1323                 if os.SameFile(fi, knownFI) {
1324                         found = append(found, ids...)
1325                 }
1326         }
1327         return found
1328 }
1329
1330 // fileWasSaved reports whether the FileHandle passed in has been saved. It
1331 // accomplishes this by checking to see if the original and current FileHandles
1332 // are both overlays, and if the current FileHandle is saved while the original
1333 // FileHandle was not saved.
1334 func fileWasSaved(originalFH, currentFH source.FileHandle) bool {
1335         c, ok := currentFH.(*overlay)
1336         if !ok || c == nil {
1337                 return true
1338         }
1339         o, ok := originalFH.(*overlay)
1340         if !ok || o == nil {
1341                 return c.saved
1342         }
1343         return !o.saved && c.saved
1344 }
1345
1346 // shouldInvalidateMetadata reparses a file's package and import declarations to
1347 // determine if the file requires a metadata reload.
1348 func (s *snapshot) shouldInvalidateMetadata(ctx context.Context, newSnapshot *snapshot, originalFH, currentFH source.FileHandle) bool {
1349         if originalFH == nil {
1350                 return true
1351         }
1352         // If the file hasn't changed, there's no need to reload.
1353         if originalFH.FileIdentity() == currentFH.FileIdentity() {
1354                 return false
1355         }
1356         // If a go.mod in the workspace has been changed, invalidate metadata.
1357         if kind := originalFH.Kind(); kind == source.Mod {
1358                 return source.InDir(filepath.Dir(s.view.rootURI.Filename()), filepath.Dir(originalFH.URI().Filename()))
1359         }
1360         // Get the original and current parsed files in order to check package name
1361         // and imports. Use the new snapshot to parse to avoid modifying the
1362         // current snapshot.
1363         original, originalErr := newSnapshot.ParseGo(ctx, originalFH, source.ParseHeader)
1364         current, currentErr := newSnapshot.ParseGo(ctx, currentFH, source.ParseHeader)
1365         if originalErr != nil || currentErr != nil {
1366                 return (originalErr == nil) != (currentErr == nil)
1367         }
1368         // Check if the package's metadata has changed. The cases handled are:
1369         //    1. A package's name has changed
1370         //    2. A file's imports have changed
1371         if original.File.Name.Name != current.File.Name.Name {
1372                 return true
1373         }
1374         importSet := make(map[string]struct{})
1375         for _, importSpec := range original.File.Imports {
1376                 importSet[importSpec.Path.Value] = struct{}{}
1377         }
1378         // If any of the current imports were not in the original imports.
1379         for _, importSpec := range current.File.Imports {
1380                 if _, ok := importSet[importSpec.Path.Value]; ok {
1381                         continue
1382                 }
1383                 // If the import path is obviously not valid, we can skip reloading
1384                 // metadata. For now, valid means properly quoted and without a
1385                 // terminal slash.
1386                 path, err := strconv.Unquote(importSpec.Path.Value)
1387                 if err != nil {
1388                         continue
1389                 }
1390                 if path == "" {
1391                         continue
1392                 }
1393                 if path[len(path)-1] == '/' {
1394                         continue
1395                 }
1396                 return true
1397         }
1398         return false
1399 }
1400
1401 func (s *snapshot) BuiltinPackage(ctx context.Context) (*source.BuiltinPackage, error) {
1402         s.AwaitInitialized(ctx)
1403
1404         if s.builtin == nil {
1405                 return nil, errors.Errorf("no builtin package for view %s", s.view.name)
1406         }
1407         d, err := s.builtin.handle.Get(ctx, s.generation, s)
1408         if err != nil {
1409                 return nil, err
1410         }
1411         data := d.(*builtinPackageData)
1412         return data.parsed, data.err
1413 }
1414
1415 func (s *snapshot) buildBuiltinPackage(ctx context.Context, goFiles []string) error {
1416         if len(goFiles) != 1 {
1417                 return errors.Errorf("only expected 1 file, got %v", len(goFiles))
1418         }
1419         uri := span.URIFromPath(goFiles[0])
1420
1421         // Get the FileHandle through the cache to avoid adding it to the snapshot
1422         // and to get the file content from disk.
1423         fh, err := s.view.session.cache.getFile(ctx, uri)
1424         if err != nil {
1425                 return err
1426         }
1427         h := s.generation.Bind(fh.FileIdentity(), func(ctx context.Context, arg memoize.Arg) interface{} {
1428                 snapshot := arg.(*snapshot)
1429
1430                 pgh := snapshot.parseGoHandle(ctx, fh, source.ParseFull)
1431                 pgf, _, err := snapshot.parseGo(ctx, pgh)
1432                 if err != nil {
1433                         return &builtinPackageData{err: err}
1434                 }
1435                 pkg, err := ast.NewPackage(snapshot.view.session.cache.fset, map[string]*ast.File{
1436                         pgf.URI.Filename(): pgf.File,
1437                 }, nil, nil)
1438                 if err != nil {
1439                         return &builtinPackageData{err: err}
1440                 }
1441                 return &builtinPackageData{
1442                         parsed: &source.BuiltinPackage{
1443                                 ParsedFile: pgf,
1444                                 Package:    pkg,
1445                         },
1446                 }
1447         }, nil)
1448         s.builtin = &builtinPackageHandle{handle: h}
1449         return nil
1450 }
1451
1452 // BuildGoplsMod generates a go.mod file for all modules in the workspace. It
1453 // bypasses any existing gopls.mod.
1454 func BuildGoplsMod(ctx context.Context, root span.URI, fs source.FileSource) (*modfile.File, error) {
1455         allModules, err := findAllModules(ctx, root)
1456         if err != nil {
1457                 return nil, err
1458         }
1459         return buildWorkspaceModFile(ctx, allModules, fs)
1460 }
1461
1462 // TODO(rfindley): move this to workspacemodule.go
1463 func buildWorkspaceModFile(ctx context.Context, modFiles map[span.URI]struct{}, fs source.FileSource) (*modfile.File, error) {
1464         file := &modfile.File{}
1465         file.AddModuleStmt("gopls-workspace")
1466
1467         paths := make(map[string]span.URI)
1468         for modURI := range modFiles {
1469                 fh, err := fs.GetFile(ctx, modURI)
1470                 if err != nil {
1471                         return nil, err
1472                 }
1473                 content, err := fh.Read()
1474                 if err != nil {
1475                         return nil, err
1476                 }
1477                 parsed, err := modfile.Parse(fh.URI().Filename(), content, nil)
1478                 if err != nil {
1479                         return nil, err
1480                 }
1481                 if file == nil || parsed.Module == nil {
1482                         return nil, fmt.Errorf("no module declaration for %s", modURI)
1483                 }
1484                 path := parsed.Module.Mod.Path
1485                 paths[path] = modURI
1486                 // If the module's path includes a major version, we expect it to have
1487                 // a matching major version.
1488                 _, majorVersion, _ := module.SplitPathVersion(path)
1489                 if majorVersion == "" {
1490                         majorVersion = "/v0"
1491                 }
1492                 majorVersion = strings.TrimLeft(majorVersion, "/.") // handle gopkg.in versions
1493                 file.AddNewRequire(path, source.WorkspaceModuleVersion(majorVersion), false)
1494                 if err := file.AddReplace(path, "", dirURI(modURI).Filename(), ""); err != nil {
1495                         return nil, err
1496                 }
1497         }
1498         // Go back through all of the modules to handle any of their replace
1499         // statements.
1500         for modURI := range modFiles {
1501                 fh, err := fs.GetFile(ctx, modURI)
1502                 if err != nil {
1503                         return nil, err
1504                 }
1505                 content, err := fh.Read()
1506                 if err != nil {
1507                         return nil, err
1508                 }
1509                 parsed, err := modfile.Parse(fh.URI().Filename(), content, nil)
1510                 if err != nil {
1511                         return nil, err
1512                 }
1513                 // If any of the workspace modules have replace directives, they need
1514                 // to be reflected in the workspace module.
1515                 for _, rep := range parsed.Replace {
1516                         // Don't replace any modules that are in our workspace--we should
1517                         // always use the version in the workspace.
1518                         if _, ok := paths[rep.Old.Path]; ok {
1519                                 continue
1520                         }
1521                         newPath := rep.New.Path
1522                         newVersion := rep.New.Version
1523                         // If a replace points to a module in the workspace, make sure we
1524                         // direct it to version of the module in the workspace.
1525                         if m, ok := paths[rep.New.Path]; ok {
1526                                 newPath = dirURI(m).Filename()
1527                                 newVersion = ""
1528                         } else if rep.New.Version == "" && !filepath.IsAbs(rep.New.Path) {
1529                                 // Make any relative paths absolute.
1530                                 newPath = filepath.Join(dirURI(modURI).Filename(), rep.New.Path)
1531                         }
1532                         if err := file.AddReplace(rep.Old.Path, rep.Old.Version, newPath, newVersion); err != nil {
1533                                 return nil, err
1534                         }
1535                 }
1536         }
1537         return file, nil
1538 }