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