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