.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 / load.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         "context"
9         "crypto/sha256"
10         "fmt"
11         "go/types"
12         "io/ioutil"
13         "os"
14         "path/filepath"
15         "sort"
16         "strings"
17         "time"
18
19         "golang.org/x/tools/go/packages"
20         "golang.org/x/tools/internal/event"
21         "golang.org/x/tools/internal/gocommand"
22         "golang.org/x/tools/internal/lsp/debug/tag"
23         "golang.org/x/tools/internal/lsp/protocol"
24         "golang.org/x/tools/internal/lsp/source"
25         "golang.org/x/tools/internal/memoize"
26         "golang.org/x/tools/internal/packagesinternal"
27         "golang.org/x/tools/internal/span"
28         errors "golang.org/x/xerrors"
29 )
30
31 // metadata holds package metadata extracted from a call to packages.Load.
32 type metadata struct {
33         id              packageID
34         pkgPath         packagePath
35         name            packageName
36         goFiles         []span.URI
37         compiledGoFiles []span.URI
38         forTest         packagePath
39         typesSizes      types.Sizes
40         errors          []packages.Error
41         deps            []packageID
42         missingDeps     map[packagePath]struct{}
43         module          *packages.Module
44         depsErrors      []*packagesinternal.PackageError
45
46         // config is the *packages.Config associated with the loaded package.
47         config *packages.Config
48 }
49
50 // load calls packages.Load for the given scopes, updating package metadata,
51 // import graph, and mapped files with the result.
52 func (s *snapshot) load(ctx context.Context, allowNetwork bool, scopes ...interface{}) error {
53         var query []string
54         var containsDir bool // for logging
55         for _, scope := range scopes {
56                 switch scope := scope.(type) {
57                 case packagePath:
58                         if isCommandLineArguments(string(scope)) {
59                                 panic("attempted to load command-line-arguments")
60                         }
61                         // The only time we pass package paths is when we're doing a
62                         // partial workspace load. In those cases, the paths came back from
63                         // go list and should already be GOPATH-vendorized when appropriate.
64                         query = append(query, string(scope))
65                 case fileURI:
66                         uri := span.URI(scope)
67                         // Don't try to load a file that doesn't exist.
68                         fh := s.FindFile(uri)
69                         if fh == nil || fh.Kind() != source.Go {
70                                 continue
71                         }
72                         query = append(query, fmt.Sprintf("file=%s", uri.Filename()))
73                 case moduleLoadScope:
74                         query = append(query, fmt.Sprintf("%s/...", scope))
75                 case viewLoadScope:
76                         // If we are outside of GOPATH, a module, or some other known
77                         // build system, don't load subdirectories.
78                         if !s.ValidBuildConfiguration() {
79                                 query = append(query, "./")
80                         } else {
81                                 query = append(query, "./...")
82                         }
83                 default:
84                         panic(fmt.Sprintf("unknown scope type %T", scope))
85                 }
86                 switch scope.(type) {
87                 case viewLoadScope, moduleLoadScope:
88                         containsDir = true
89                 }
90         }
91         if len(query) == 0 {
92                 return nil
93         }
94         sort.Strings(query) // for determinism
95
96         ctx, done := event.Start(ctx, "cache.view.load", tag.Query.Of(query))
97         defer done()
98
99         flags := source.LoadWorkspace
100         if allowNetwork {
101                 flags |= source.AllowNetwork
102         }
103         _, inv, cleanup, err := s.goCommandInvocation(ctx, flags, &gocommand.Invocation{
104                 WorkingDir: s.view.rootURI.Filename(),
105         })
106         if err != nil {
107                 return err
108         }
109
110         // Set a last resort deadline on packages.Load since it calls the go
111         // command, which may hang indefinitely if it has a bug. golang/go#42132
112         // and golang/go#42255 have more context.
113         ctx, cancel := context.WithTimeout(ctx, 15*time.Minute)
114         defer cancel()
115
116         cfg := s.config(ctx, inv)
117         pkgs, err := packages.Load(cfg, query...)
118         cleanup()
119
120         // If the context was canceled, return early. Otherwise, we might be
121         // type-checking an incomplete result. Check the context directly,
122         // because go/packages adds extra information to the error.
123         if ctx.Err() != nil {
124                 return ctx.Err()
125         }
126         if err != nil {
127                 event.Error(ctx, "go/packages.Load", err, tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
128         } else {
129                 event.Log(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
130         }
131         if len(pkgs) == 0 {
132                 if err == nil {
133                         err = fmt.Errorf("no packages returned")
134                 }
135                 return errors.Errorf("%v: %w", err, source.PackagesLoadError)
136         }
137         for _, pkg := range pkgs {
138                 if !containsDir || s.view.Options().VerboseOutput {
139                         event.Log(ctx, "go/packages.Load",
140                                 tag.Snapshot.Of(s.ID()),
141                                 tag.Package.Of(pkg.ID),
142                                 tag.Files.Of(pkg.CompiledGoFiles))
143                 }
144                 // Ignore packages with no sources, since we will never be able to
145                 // correctly invalidate that metadata.
146                 if len(pkg.GoFiles) == 0 && len(pkg.CompiledGoFiles) == 0 {
147                         continue
148                 }
149                 // Special case for the builtin package, as it has no dependencies.
150                 if pkg.PkgPath == "builtin" {
151                         if err := s.buildBuiltinPackage(ctx, pkg.GoFiles); err != nil {
152                                 return err
153                         }
154                         continue
155                 }
156                 // Skip test main packages.
157                 if isTestMain(pkg, s.view.gocache) {
158                         continue
159                 }
160                 // Skip filtered packages. They may be added anyway if they're
161                 // dependencies of non-filtered packages.
162                 if s.view.allFilesExcluded(pkg) {
163                         continue
164                 }
165                 // Set the metadata for this package.
166                 m, err := s.setMetadata(ctx, packagePath(pkg.PkgPath), pkg, cfg, map[packageID]struct{}{})
167                 if err != nil {
168                         return err
169                 }
170                 if _, err := s.buildPackageHandle(ctx, m.id, s.workspaceParseMode(m.id)); err != nil {
171                         return err
172                 }
173         }
174         // Rebuild the import graph when the metadata is updated.
175         s.clearAndRebuildImportGraph()
176
177         return nil
178 }
179
180 // workspaceLayoutErrors returns a diagnostic for every open file, as well as
181 // an error message if there are no open files.
182 func (s *snapshot) workspaceLayoutError(ctx context.Context) *source.CriticalError {
183         if len(s.workspace.getKnownModFiles()) == 0 {
184                 return nil
185         }
186         if s.view.userGo111Module == off {
187                 return nil
188         }
189         if s.workspace.moduleSource != legacyWorkspace {
190                 return nil
191         }
192         // If the user has one module per view, there is nothing to warn about.
193         if s.ValidBuildConfiguration() && len(s.workspace.getKnownModFiles()) == 1 {
194                 return nil
195         }
196
197         // Apply diagnostics about the workspace configuration to relevant open
198         // files.
199         openFiles := s.openFiles()
200
201         // If the snapshot does not have a valid build configuration, it may be
202         // that the user has opened a directory that contains multiple modules.
203         // Check for that an warn about it.
204         if !s.ValidBuildConfiguration() {
205                 msg := `gopls requires a module at the root of your workspace.
206 You can work with multiple modules by opening each one as a workspace folder.
207 Improvements to this workflow will be coming soon, and you can learn more here:
208 https://github.com/golang/tools/blob/master/gopls/doc/workspace.md.`
209                 return &source.CriticalError{
210                         MainError: errors.Errorf(msg),
211                         DiagList:  s.applyCriticalErrorToFiles(ctx, msg, openFiles),
212                 }
213         }
214
215         // If the user has one active go.mod file, they may still be editing files
216         // in nested modules. Check the module of each open file and add warnings
217         // that the nested module must be opened as a workspace folder.
218         if len(s.workspace.getActiveModFiles()) == 1 {
219                 // Get the active root go.mod file to compare against.
220                 var rootModURI span.URI
221                 for uri := range s.workspace.getActiveModFiles() {
222                         rootModURI = uri
223                 }
224                 nestedModules := map[string][]source.VersionedFileHandle{}
225                 for _, fh := range openFiles {
226                         modURI := moduleForURI(s.workspace.knownModFiles, fh.URI())
227                         if modURI != rootModURI {
228                                 modDir := filepath.Dir(modURI.Filename())
229                                 nestedModules[modDir] = append(nestedModules[modDir], fh)
230                         }
231                 }
232                 // Add a diagnostic to each file in a nested module to mark it as
233                 // "orphaned". Don't show a general diagnostic in the progress bar,
234                 // because the user may still want to edit a file in a nested module.
235                 var srcDiags []*source.Diagnostic
236                 for modDir, uris := range nestedModules {
237                         msg := fmt.Sprintf(`This file is in %s, which is a nested module in the %s module.
238 gopls currently requires one module per workspace folder.
239 Please open %s as a separate workspace folder.
240 You can learn more here: https://github.com/golang/tools/blob/master/gopls/doc/workspace.md.
241 `, modDir, filepath.Dir(rootModURI.Filename()), modDir)
242                         srcDiags = append(srcDiags, s.applyCriticalErrorToFiles(ctx, msg, uris)...)
243                 }
244                 if len(srcDiags) != 0 {
245                         return &source.CriticalError{
246                                 MainError: errors.Errorf(`You are working in a nested module.
247 Please open it as a separate workspace folder. Learn more:
248 https://github.com/golang/tools/blob/master/gopls/doc/workspace.md.`),
249                                 DiagList: srcDiags,
250                         }
251                 }
252         }
253         return nil
254 }
255
256 func (s *snapshot) applyCriticalErrorToFiles(ctx context.Context, msg string, files []source.VersionedFileHandle) []*source.Diagnostic {
257         var srcDiags []*source.Diagnostic
258         for _, fh := range files {
259                 // Place the diagnostics on the package or module declarations.
260                 var rng protocol.Range
261                 switch fh.Kind() {
262                 case source.Go:
263                         if pgf, err := s.ParseGo(ctx, fh, source.ParseHeader); err == nil {
264                                 pkgDecl := span.NewRange(s.FileSet(), pgf.File.Package, pgf.File.Name.End())
265                                 if spn, err := pkgDecl.Span(); err == nil {
266                                         rng, _ = pgf.Mapper.Range(spn)
267                                 }
268                         }
269                 case source.Mod:
270                         if pmf, err := s.ParseMod(ctx, fh); err == nil {
271                                 if pmf.File.Module != nil && pmf.File.Module.Syntax != nil {
272                                         rng, _ = rangeFromPositions(pmf.Mapper, pmf.File.Module.Syntax.Start, pmf.File.Module.Syntax.End)
273                                 }
274                         }
275                 }
276                 srcDiags = append(srcDiags, &source.Diagnostic{
277                         URI:      fh.URI(),
278                         Range:    rng,
279                         Severity: protocol.SeverityError,
280                         Source:   source.ListError,
281                         Message:  msg,
282                 })
283         }
284         return srcDiags
285 }
286
287 type workspaceDirKey string
288
289 type workspaceDirData struct {
290         dir string
291         err error
292 }
293
294 // getWorkspaceDir gets the URI for the workspace directory associated with
295 // this snapshot. The workspace directory is a temp directory containing the
296 // go.mod file computed from all active modules.
297 func (s *snapshot) getWorkspaceDir(ctx context.Context) (span.URI, error) {
298         s.mu.Lock()
299         h := s.workspaceDirHandle
300         s.mu.Unlock()
301         if h != nil {
302                 return getWorkspaceDir(ctx, h, s.generation)
303         }
304         file, err := s.workspace.modFile(ctx, s)
305         if err != nil {
306                 return "", err
307         }
308         hash := sha256.New()
309         modContent, err := file.Format()
310         if err != nil {
311                 return "", err
312         }
313         sumContent, err := s.workspace.sumFile(ctx, s)
314         if err != nil {
315                 return "", err
316         }
317         hash.Write(modContent)
318         hash.Write(sumContent)
319         key := workspaceDirKey(hash.Sum(nil))
320         s.mu.Lock()
321         h = s.generation.Bind(key, func(context.Context, memoize.Arg) interface{} {
322                 tmpdir, err := ioutil.TempDir("", "gopls-workspace-mod")
323                 if err != nil {
324                         return &workspaceDirData{err: err}
325                 }
326
327                 for name, content := range map[string][]byte{
328                         "go.mod": modContent,
329                         "go.sum": sumContent,
330                 } {
331                         filename := filepath.Join(tmpdir, name)
332                         if err := ioutil.WriteFile(filename, content, 0644); err != nil {
333                                 os.RemoveAll(tmpdir)
334                                 return &workspaceDirData{err: err}
335                         }
336                 }
337
338                 return &workspaceDirData{dir: tmpdir}
339         }, func(v interface{}) {
340                 d := v.(*workspaceDirData)
341                 if d.dir != "" {
342                         if err := os.RemoveAll(d.dir); err != nil {
343                                 event.Error(context.Background(), "cleaning workspace dir", err)
344                         }
345                 }
346         })
347         s.workspaceDirHandle = h
348         s.mu.Unlock()
349         return getWorkspaceDir(ctx, h, s.generation)
350 }
351
352 func getWorkspaceDir(ctx context.Context, h *memoize.Handle, g *memoize.Generation) (span.URI, error) {
353         v, err := h.Get(ctx, g, nil)
354         if err != nil {
355                 return "", err
356         }
357         return span.URIFromPath(v.(*workspaceDirData).dir), nil
358 }
359
360 // setMetadata extracts metadata from pkg and records it in s. It
361 // recurses through pkg.Imports to ensure that metadata exists for all
362 // dependencies.
363 func (s *snapshot) setMetadata(ctx context.Context, pkgPath packagePath, pkg *packages.Package, cfg *packages.Config, seen map[packageID]struct{}) (*metadata, error) {
364         id := packageID(pkg.ID)
365         if _, ok := seen[id]; ok {
366                 return nil, errors.Errorf("import cycle detected: %q", id)
367         }
368         // Recreate the metadata rather than reusing it to avoid locking.
369         m := &metadata{
370                 id:         id,
371                 pkgPath:    pkgPath,
372                 name:       packageName(pkg.Name),
373                 forTest:    packagePath(packagesinternal.GetForTest(pkg)),
374                 typesSizes: pkg.TypesSizes,
375                 config:     cfg,
376                 module:     pkg.Module,
377                 depsErrors: packagesinternal.GetDepsErrors(pkg),
378         }
379
380         for _, err := range pkg.Errors {
381                 // Filter out parse errors from go list. We'll get them when we
382                 // actually parse, and buggy overlay support may generate spurious
383                 // errors. (See TestNewModule_Issue38207.)
384                 if strings.Contains(err.Msg, "expected '") {
385                         continue
386                 }
387                 m.errors = append(m.errors, err)
388         }
389
390         for _, filename := range pkg.CompiledGoFiles {
391                 uri := span.URIFromPath(filename)
392                 m.compiledGoFiles = append(m.compiledGoFiles, uri)
393                 s.addID(uri, m.id)
394         }
395         for _, filename := range pkg.GoFiles {
396                 uri := span.URIFromPath(filename)
397                 m.goFiles = append(m.goFiles, uri)
398                 s.addID(uri, m.id)
399         }
400
401         // TODO(rstambler): is this still necessary?
402         copied := map[packageID]struct{}{
403                 id: {},
404         }
405         for k, v := range seen {
406                 copied[k] = v
407         }
408         for importPath, importPkg := range pkg.Imports {
409                 importPkgPath := packagePath(importPath)
410                 importID := packageID(importPkg.ID)
411
412                 m.deps = append(m.deps, importID)
413
414                 // Don't remember any imports with significant errors.
415                 if importPkgPath != "unsafe" && len(importPkg.CompiledGoFiles) == 0 {
416                         if m.missingDeps == nil {
417                                 m.missingDeps = make(map[packagePath]struct{})
418                         }
419                         m.missingDeps[importPkgPath] = struct{}{}
420                         continue
421                 }
422                 if s.getMetadata(importID) == nil {
423                         if _, err := s.setMetadata(ctx, importPkgPath, importPkg, cfg, copied); err != nil {
424                                 event.Error(ctx, "error in dependency", err)
425                         }
426                 }
427         }
428
429         // Add the metadata to the cache.
430         s.mu.Lock()
431         defer s.mu.Unlock()
432
433         // TODO: We should make sure not to set duplicate metadata,
434         // and instead panic here. This can be done by making sure not to
435         // reset metadata information for packages we've already seen.
436         if original, ok := s.metadata[m.id]; ok {
437                 m = original
438         } else {
439                 s.metadata[m.id] = m
440         }
441
442         // Set the workspace packages. If any of the package's files belong to the
443         // view, then the package may be a workspace package.
444         for _, uri := range append(m.compiledGoFiles, m.goFiles...) {
445                 if !s.view.contains(uri) {
446                         continue
447                 }
448
449                 // The package's files are in this view. It may be a workspace package.
450                 if strings.Contains(string(uri), "/vendor/") {
451                         // Vendored packages are not likely to be interesting to the user.
452                         continue
453                 }
454
455                 switch {
456                 case m.forTest == "":
457                         // A normal package.
458                         s.workspacePackages[m.id] = pkgPath
459                 case m.forTest == m.pkgPath, m.forTest+"_test" == m.pkgPath:
460                         // The test variant of some workspace package or its x_test.
461                         // To load it, we need to load the non-test variant with -test.
462                         s.workspacePackages[m.id] = m.forTest
463                 default:
464                         // A test variant of some intermediate package. We don't care about it.
465                 }
466         }
467         return m, nil
468 }
469
470 func isTestMain(pkg *packages.Package, gocache string) bool {
471         // Test mains must have an import path that ends with ".test".
472         if !strings.HasSuffix(pkg.PkgPath, ".test") {
473                 return false
474         }
475         // Test main packages are always named "main".
476         if pkg.Name != "main" {
477                 return false
478         }
479         // Test mains always have exactly one GoFile that is in the build cache.
480         if len(pkg.GoFiles) > 1 {
481                 return false
482         }
483         if !source.InDir(gocache, pkg.GoFiles[0]) {
484                 return false
485         }
486         return true
487 }