Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / cache / 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         "fmt"
10         "go/types"
11         "io/ioutil"
12         "os"
13         "path/filepath"
14         "sort"
15         "strings"
16
17         "golang.org/x/tools/go/packages"
18         "golang.org/x/tools/internal/event"
19         "golang.org/x/tools/internal/lsp/debug/tag"
20         "golang.org/x/tools/internal/lsp/source"
21         "golang.org/x/tools/internal/packagesinternal"
22         "golang.org/x/tools/internal/span"
23         errors "golang.org/x/xerrors"
24 )
25
26 // metadata holds package metadata extracted from a call to packages.Load.
27 type metadata struct {
28         id              packageID
29         pkgPath         packagePath
30         name            packageName
31         goFiles         []span.URI
32         compiledGoFiles []span.URI
33         forTest         packagePath
34         typesSizes      types.Sizes
35         errors          []packages.Error
36         deps            []packageID
37         missingDeps     map[packagePath]struct{}
38         module          *packages.Module
39
40         // config is the *packages.Config associated with the loaded package.
41         config *packages.Config
42 }
43
44 // load calls packages.Load for the given scopes, updating package metadata,
45 // import graph, and mapped files with the result.
46 func (s *snapshot) load(ctx context.Context, scopes ...interface{}) error {
47         var query []string
48         var containsDir bool // for logging
49         for _, scope := range scopes {
50                 switch scope := scope.(type) {
51                 case packagePath:
52                         if scope == "command-line-arguments" {
53                                 panic("attempted to load command-line-arguments")
54                         }
55                         // The only time we pass package paths is when we're doing a
56                         // partial workspace load. In those cases, the paths came back from
57                         // go list and should already be GOPATH-vendorized when appropriate.
58                         query = append(query, string(scope))
59                 case fileURI:
60                         uri := span.URI(scope)
61                         // Don't try to load a file that doesn't exist.
62                         fh := s.FindFile(uri)
63                         if fh == nil || fh.Kind() != source.Go {
64                                 continue
65                         }
66                         query = append(query, fmt.Sprintf("file=%s", uri.Filename()))
67                 case moduleLoadScope:
68                         query = append(query, fmt.Sprintf("%s/...", scope))
69                 case viewLoadScope:
70                         // If we are outside of GOPATH, a module, or some other known
71                         // build system, don't load subdirectories.
72                         if !s.ValidBuildConfiguration() {
73                                 query = append(query, "./")
74                         } else {
75                                 query = append(query, "./...")
76                         }
77                 default:
78                         panic(fmt.Sprintf("unknown scope type %T", scope))
79                 }
80                 switch scope.(type) {
81                 case viewLoadScope:
82                         containsDir = true
83                 }
84         }
85         if len(query) == 0 {
86                 return nil
87         }
88         sort.Strings(query) // for determinism
89
90         ctx, done := event.Start(ctx, "cache.view.load", tag.Query.Of(query))
91         defer done()
92
93         cleanup := func() {}
94         wdir := s.view.rootURI.Filename()
95
96         var modFile string
97         var modURI span.URI
98         var modContent []byte
99         switch {
100         case s.workspaceMode()&usesWorkspaceModule != 0:
101                 var (
102                         tmpDir span.URI
103                         err    error
104                 )
105                 tmpDir, cleanup, err = s.tempWorkspaceModule(ctx)
106                 if err != nil {
107                         return err
108                 }
109                 wdir = tmpDir.Filename()
110                 modURI = span.URIFromPath(filepath.Join(wdir, "go.mod"))
111                 modContent, err = ioutil.ReadFile(modURI.Filename())
112                 if err != nil {
113                         return err
114                 }
115         case s.workspaceMode()&tempModfile != 0:
116                 // -modfile is unsupported when there are > 1 modules in the workspace.
117                 if len(s.modules) != 1 {
118                         panic(fmt.Sprintf("unsupported use of -modfile, expected 1 module, got %v", len(s.modules)))
119                 }
120                 var mod *moduleRoot
121                 for _, m := range s.modules { // range to access the only element
122                         mod = m
123                 }
124                 modURI = mod.modURI
125                 modFH, err := s.GetFile(ctx, mod.modURI)
126                 if err != nil {
127                         return err
128                 }
129                 modContent, err = modFH.Read()
130                 if err != nil {
131                         return err
132                 }
133                 var sumFH source.FileHandle
134                 if mod.sumURI != "" {
135                         sumFH, err = s.GetFile(ctx, mod.sumURI)
136                         if err != nil {
137                                 return err
138                         }
139                 }
140                 var tmpURI span.URI
141                 tmpURI, cleanup, err = tempModFile(modFH, sumFH)
142                 if err != nil {
143                         return err
144                 }
145                 modFile = tmpURI.Filename()
146         }
147
148         cfg := s.config(ctx, wdir)
149         packagesinternal.SetModFile(cfg, modFile)
150         modMod, err := s.needsModEqualsMod(ctx, modURI, modContent)
151         if err != nil {
152                 return err
153         }
154         if modMod {
155                 packagesinternal.SetModFlag(cfg, "mod")
156         }
157
158         pkgs, err := packages.Load(cfg, query...)
159         cleanup()
160
161         // If the context was canceled, return early. Otherwise, we might be
162         // type-checking an incomplete result. Check the context directly,
163         // because go/packages adds extra information to the error.
164         if ctx.Err() != nil {
165                 return ctx.Err()
166         }
167         if err != nil {
168                 // Match on common error messages. This is really hacky, but I'm not sure
169                 // of any better way. This can be removed when golang/go#39164 is resolved.
170                 if strings.Contains(err.Error(), "inconsistent vendoring") {
171                         return source.InconsistentVendoring
172                 }
173                 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)))
174         } else {
175                 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)))
176         }
177         if len(pkgs) == 0 {
178                 if err != nil {
179                         // Try to extract the error into a diagnostic.
180                         if srcErrs := s.parseLoadError(ctx, err); srcErrs != nil {
181                                 return srcErrs
182                         }
183                 } else {
184                         err = fmt.Errorf("no packages returned")
185                 }
186                 return errors.Errorf("%v: %w", err, source.PackagesLoadError)
187         }
188         for _, pkg := range pkgs {
189                 if !containsDir || s.view.Options().VerboseOutput {
190                         event.Log(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.PackagePath.Of(pkg.PkgPath), tag.Files.Of(pkg.CompiledGoFiles))
191                 }
192                 // Ignore packages with no sources, since we will never be able to
193                 // correctly invalidate that metadata.
194                 if len(pkg.GoFiles) == 0 && len(pkg.CompiledGoFiles) == 0 {
195                         continue
196                 }
197                 // Special case for the builtin package, as it has no dependencies.
198                 if pkg.PkgPath == "builtin" {
199                         if err := s.buildBuiltinPackage(ctx, pkg.GoFiles); err != nil {
200                                 return err
201                         }
202                         continue
203                 }
204                 // Skip test main packages.
205                 if isTestMain(pkg, s.view.gocache) {
206                         continue
207                 }
208                 // Set the metadata for this package.
209                 m, err := s.setMetadata(ctx, packagePath(pkg.PkgPath), pkg, cfg, map[packageID]struct{}{})
210                 if err != nil {
211                         return err
212                 }
213                 if _, err := s.buildPackageHandle(ctx, m.id, s.workspaceParseMode(m.id)); err != nil {
214                         return err
215                 }
216         }
217         // Rebuild the import graph when the metadata is updated.
218         s.clearAndRebuildImportGraph()
219
220         return nil
221 }
222
223 func (s *snapshot) parseLoadError(ctx context.Context, loadErr error) *source.ErrorList {
224         var srcErrs *source.ErrorList
225         for _, uri := range s.ModFiles() {
226                 fh, err := s.GetFile(ctx, uri)
227                 if err != nil {
228                         continue
229                 }
230                 srcErr := extractGoCommandError(ctx, s, fh, loadErr)
231                 if srcErr == nil {
232                         continue
233                 }
234                 if srcErrs == nil {
235                         srcErrs = &source.ErrorList{}
236                 }
237                 *srcErrs = append(*srcErrs, srcErr)
238         }
239         return srcErrs
240 }
241
242 // tempWorkspaceModule creates a temporary directory for use with
243 // packages.Loads that occur from within the workspace module.
244 func (s *snapshot) tempWorkspaceModule(ctx context.Context) (_ span.URI, cleanup func(), err error) {
245         cleanup = func() {}
246         if s.workspaceMode()&usesWorkspaceModule == 0 {
247                 return "", cleanup, nil
248         }
249         wsModuleHandle, err := s.getWorkspaceModuleHandle(ctx)
250         if err != nil {
251                 return "", nil, err
252         }
253         file, err := wsModuleHandle.build(ctx, s)
254         if err != nil {
255                 return "", nil, err
256         }
257         content, err := file.Format()
258         if err != nil {
259                 return "", cleanup, err
260         }
261         // Create a temporary working directory for the go command that contains
262         // the workspace module file.
263         name, err := ioutil.TempDir("", "gopls-mod")
264         if err != nil {
265                 return "", cleanup, err
266         }
267         cleanup = func() {
268                 os.RemoveAll(name)
269         }
270         filename := filepath.Join(name, "go.mod")
271         if err := ioutil.WriteFile(filename, content, 0644); err != nil {
272                 cleanup()
273                 return "", cleanup, err
274         }
275         return span.URIFromPath(filepath.Dir(filename)), cleanup, nil
276 }
277
278 // setMetadata extracts metadata from pkg and records it in s. It
279 // recurses through pkg.Imports to ensure that metadata exists for all
280 // dependencies.
281 func (s *snapshot) setMetadata(ctx context.Context, pkgPath packagePath, pkg *packages.Package, cfg *packages.Config, seen map[packageID]struct{}) (*metadata, error) {
282         id := packageID(pkg.ID)
283         if _, ok := seen[id]; ok {
284                 return nil, errors.Errorf("import cycle detected: %q", id)
285         }
286         // Recreate the metadata rather than reusing it to avoid locking.
287         m := &metadata{
288                 id:         id,
289                 pkgPath:    pkgPath,
290                 name:       packageName(pkg.Name),
291                 forTest:    packagePath(packagesinternal.GetForTest(pkg)),
292                 typesSizes: pkg.TypesSizes,
293                 errors:     pkg.Errors,
294                 config:     cfg,
295                 module:     pkg.Module,
296         }
297
298         for _, filename := range pkg.CompiledGoFiles {
299                 uri := span.URIFromPath(filename)
300                 m.compiledGoFiles = append(m.compiledGoFiles, uri)
301                 s.addID(uri, m.id)
302         }
303         for _, filename := range pkg.GoFiles {
304                 uri := span.URIFromPath(filename)
305                 m.goFiles = append(m.goFiles, uri)
306                 s.addID(uri, m.id)
307         }
308
309         // TODO(rstambler): is this still necessary?
310         copied := map[packageID]struct{}{
311                 id: {},
312         }
313         for k, v := range seen {
314                 copied[k] = v
315         }
316         for importPath, importPkg := range pkg.Imports {
317                 importPkgPath := packagePath(importPath)
318                 importID := packageID(importPkg.ID)
319
320                 m.deps = append(m.deps, importID)
321
322                 // Don't remember any imports with significant errors.
323                 if importPkgPath != "unsafe" && len(importPkg.CompiledGoFiles) == 0 {
324                         if m.missingDeps == nil {
325                                 m.missingDeps = make(map[packagePath]struct{})
326                         }
327                         m.missingDeps[importPkgPath] = struct{}{}
328                         continue
329                 }
330                 if s.getMetadata(importID) == nil {
331                         if _, err := s.setMetadata(ctx, importPkgPath, importPkg, cfg, copied); err != nil {
332                                 event.Error(ctx, "error in dependency", err)
333                         }
334                 }
335         }
336
337         // Add the metadata to the cache.
338         s.mu.Lock()
339         defer s.mu.Unlock()
340
341         // TODO: We should make sure not to set duplicate metadata,
342         // and instead panic here. This can be done by making sure not to
343         // reset metadata information for packages we've already seen.
344         if original, ok := s.metadata[m.id]; ok {
345                 m = original
346         } else {
347                 s.metadata[m.id] = m
348         }
349
350         // Set the workspace packages. If any of the package's files belong to the
351         // view, then the package may be a workspace package.
352         for _, uri := range append(m.compiledGoFiles, m.goFiles...) {
353                 if !s.view.contains(uri) {
354                         continue
355                 }
356
357                 // The package's files are in this view. It may be a workspace package.
358                 if strings.Contains(string(uri), "/vendor/") {
359                         // Vendored packages are not likely to be interesting to the user.
360                         continue
361                 }
362
363                 switch {
364                 case m.forTest == "":
365                         // A normal package.
366                         s.workspacePackages[m.id] = pkgPath
367                 case m.forTest == m.pkgPath, m.forTest+"_test" == m.pkgPath:
368                         // The test variant of some workspace package or its x_test.
369                         // To load it, we need to load the non-test variant with -test.
370                         s.workspacePackages[m.id] = m.forTest
371                 default:
372                         // A test variant of some intermediate package. We don't care about it.
373                 }
374         }
375         return m, nil
376 }
377
378 func isTestMain(pkg *packages.Package, gocache string) bool {
379         // Test mains must have an import path that ends with ".test".
380         if !strings.HasSuffix(pkg.PkgPath, ".test") {
381                 return false
382         }
383         // Test main packages are always named "main".
384         if pkg.Name != "main" {
385                 return false
386         }
387         // Test mains always have exactly one GoFile that is in the build cache.
388         if len(pkg.GoFiles) > 1 {
389                 return false
390         }
391         if !strings.HasPrefix(pkg.GoFiles[0], gocache) {
392                 return false
393         }
394         return true
395 }