.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / mvdan.cc / gofumpt@v0.1.0 / gofumports / internal / imports / mod.go
1 package imports
2
3 import (
4         "bytes"
5         "context"
6         "encoding/json"
7         "fmt"
8         "io/ioutil"
9         "os"
10         "path"
11         "path/filepath"
12         "regexp"
13         "sort"
14         "strconv"
15         "strings"
16
17         "golang.org/x/mod/module"
18         "mvdan.cc/gofumpt/gofumports/internal/gocommand"
19         "mvdan.cc/gofumpt/gofumports/internal/gopathwalk"
20 )
21
22 // ModuleResolver implements resolver for modules using the go command as little
23 // as feasible.
24 type ModuleResolver struct {
25         env            *ProcessEnv
26         moduleCacheDir string
27         dummyVendorMod *gocommand.ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory.
28         roots          []gopathwalk.Root
29         scanSema       chan struct{} // scanSema prevents concurrent scans and guards scannedRoots.
30         scannedRoots   map[gopathwalk.Root]bool
31
32         initialized   bool
33         main          *gocommand.ModuleJSON
34         modsByModPath []*gocommand.ModuleJSON // All modules, ordered by # of path components in module Path...
35         modsByDir     []*gocommand.ModuleJSON // ...or Dir.
36
37         // moduleCacheCache stores information about the module cache.
38         moduleCacheCache *dirInfoCache
39         otherCache       *dirInfoCache
40 }
41
42 func newModuleResolver(e *ProcessEnv) *ModuleResolver {
43         r := &ModuleResolver{
44                 env:      e,
45                 scanSema: make(chan struct{}, 1),
46         }
47         r.scanSema <- struct{}{}
48         return r
49 }
50
51 func (r *ModuleResolver) init() error {
52         if r.initialized {
53                 return nil
54         }
55
56         goenv, err := r.env.goEnv()
57         if err != nil {
58                 return err
59         }
60         inv := gocommand.Invocation{
61                 BuildFlags: r.env.BuildFlags,
62                 ModFlag:    r.env.ModFlag,
63                 ModFile:    r.env.ModFile,
64                 Env:        r.env.env(),
65                 Logf:       r.env.Logf,
66                 WorkingDir: r.env.WorkingDir,
67         }
68         mainMod, vendorEnabled, err := gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner)
69         if err != nil {
70                 return err
71         }
72
73         if mainMod != nil && vendorEnabled {
74                 // Vendor mode is on, so all the non-Main modules are irrelevant,
75                 // and we need to search /vendor for everything.
76                 r.main = mainMod
77                 r.dummyVendorMod = &gocommand.ModuleJSON{
78                         Path: "",
79                         Dir:  filepath.Join(mainMod.Dir, "vendor"),
80                 }
81                 r.modsByModPath = []*gocommand.ModuleJSON{mainMod, r.dummyVendorMod}
82                 r.modsByDir = []*gocommand.ModuleJSON{mainMod, r.dummyVendorMod}
83         } else {
84                 // Vendor mode is off, so run go list -m ... to find everything.
85                 r.initAllMods()
86         }
87
88         if gmc := r.env.Env["GOMODCACHE"]; gmc != "" {
89                 r.moduleCacheDir = gmc
90         } else {
91                 gopaths := filepath.SplitList(goenv["GOPATH"])
92                 if len(gopaths) == 0 {
93                         return fmt.Errorf("empty GOPATH")
94                 }
95                 r.moduleCacheDir = filepath.Join(gopaths[0], "/pkg/mod")
96         }
97
98         sort.Slice(r.modsByModPath, func(i, j int) bool {
99                 count := func(x int) int {
100                         return strings.Count(r.modsByModPath[x].Path, "/")
101                 }
102                 return count(j) < count(i) // descending order
103         })
104         sort.Slice(r.modsByDir, func(i, j int) bool {
105                 count := func(x int) int {
106                         return strings.Count(r.modsByDir[x].Dir, "/")
107                 }
108                 return count(j) < count(i) // descending order
109         })
110
111         r.roots = []gopathwalk.Root{
112                 {filepath.Join(goenv["GOROOT"], "/src"), gopathwalk.RootGOROOT},
113         }
114         if r.main != nil {
115                 r.roots = append(r.roots, gopathwalk.Root{r.main.Dir, gopathwalk.RootCurrentModule})
116         }
117         if vendorEnabled {
118                 r.roots = append(r.roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther})
119         } else {
120                 addDep := func(mod *gocommand.ModuleJSON) {
121                         if mod.Replace == nil {
122                                 // This is redundant with the cache, but we'll skip it cheaply enough.
123                                 r.roots = append(r.roots, gopathwalk.Root{mod.Dir, gopathwalk.RootModuleCache})
124                         } else {
125                                 r.roots = append(r.roots, gopathwalk.Root{mod.Dir, gopathwalk.RootOther})
126                         }
127                 }
128                 // Walk dependent modules before scanning the full mod cache, direct deps first.
129                 for _, mod := range r.modsByModPath {
130                         if !mod.Indirect && !mod.Main {
131                                 addDep(mod)
132                         }
133                 }
134                 for _, mod := range r.modsByModPath {
135                         if mod.Indirect && !mod.Main {
136                                 addDep(mod)
137                         }
138                 }
139                 r.roots = append(r.roots, gopathwalk.Root{r.moduleCacheDir, gopathwalk.RootModuleCache})
140         }
141
142         r.scannedRoots = map[gopathwalk.Root]bool{}
143         if r.moduleCacheCache == nil {
144                 r.moduleCacheCache = &dirInfoCache{
145                         dirs:      map[string]*directoryPackageInfo{},
146                         listeners: map[*int]cacheListener{},
147                 }
148         }
149         if r.otherCache == nil {
150                 r.otherCache = &dirInfoCache{
151                         dirs:      map[string]*directoryPackageInfo{},
152                         listeners: map[*int]cacheListener{},
153                 }
154         }
155         r.initialized = true
156         return nil
157 }
158
159 func (r *ModuleResolver) initAllMods() error {
160         stdout, err := r.env.invokeGo(context.TODO(), "list", "-m", "-json", "...")
161         if err != nil {
162                 return err
163         }
164         for dec := json.NewDecoder(stdout); dec.More(); {
165                 mod := &gocommand.ModuleJSON{}
166                 if err := dec.Decode(mod); err != nil {
167                         return err
168                 }
169                 if mod.Dir == "" {
170                         if r.env.Logf != nil {
171                                 r.env.Logf("module %v has not been downloaded and will be ignored", mod.Path)
172                         }
173                         // Can't do anything with a module that's not downloaded.
174                         continue
175                 }
176                 // golang/go#36193: the go command doesn't always clean paths.
177                 mod.Dir = filepath.Clean(mod.Dir)
178                 r.modsByModPath = append(r.modsByModPath, mod)
179                 r.modsByDir = append(r.modsByDir, mod)
180                 if mod.Main {
181                         r.main = mod
182                 }
183         }
184         return nil
185 }
186
187 func (r *ModuleResolver) ClearForNewScan() {
188         <-r.scanSema
189         r.scannedRoots = map[gopathwalk.Root]bool{}
190         r.otherCache = &dirInfoCache{
191                 dirs:      map[string]*directoryPackageInfo{},
192                 listeners: map[*int]cacheListener{},
193         }
194         r.scanSema <- struct{}{}
195 }
196
197 func (r *ModuleResolver) ClearForNewMod() {
198         <-r.scanSema
199         *r = ModuleResolver{
200                 env:              r.env,
201                 moduleCacheCache: r.moduleCacheCache,
202                 otherCache:       r.otherCache,
203                 scanSema:         r.scanSema,
204         }
205         r.init()
206         r.scanSema <- struct{}{}
207 }
208
209 // findPackage returns the module and directory that contains the package at
210 // the given import path, or returns nil, "" if no module is in scope.
211 func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON, string) {
212         // This can't find packages in the stdlib, but that's harmless for all
213         // the existing code paths.
214         for _, m := range r.modsByModPath {
215                 if !strings.HasPrefix(importPath, m.Path) {
216                         continue
217                 }
218                 pathInModule := importPath[len(m.Path):]
219                 pkgDir := filepath.Join(m.Dir, pathInModule)
220                 if r.dirIsNestedModule(pkgDir, m) {
221                         continue
222                 }
223
224                 if info, ok := r.cacheLoad(pkgDir); ok {
225                         if loaded, err := info.reachedStatus(nameLoaded); loaded {
226                                 if err != nil {
227                                         continue // No package in this dir.
228                                 }
229                                 return m, pkgDir
230                         }
231                         if scanned, err := info.reachedStatus(directoryScanned); scanned && err != nil {
232                                 continue // Dir is unreadable, etc.
233                         }
234                         // This is slightly wrong: a directory doesn't have to have an
235                         // importable package to count as a package for package-to-module
236                         // resolution. package main or _test files should count but
237                         // don't.
238                         // TODO(heschi): fix this.
239                         if _, err := r.cachePackageName(info); err == nil {
240                                 return m, pkgDir
241                         }
242                 }
243
244                 // Not cached. Read the filesystem.
245                 pkgFiles, err := ioutil.ReadDir(pkgDir)
246                 if err != nil {
247                         continue
248                 }
249                 // A module only contains a package if it has buildable go
250                 // files in that directory. If not, it could be provided by an
251                 // outer module. See #29736.
252                 for _, fi := range pkgFiles {
253                         if ok, _ := r.env.matchFile(pkgDir, fi.Name()); ok {
254                                 return m, pkgDir
255                         }
256                 }
257         }
258         return nil, ""
259 }
260
261 func (r *ModuleResolver) cacheLoad(dir string) (directoryPackageInfo, bool) {
262         if info, ok := r.moduleCacheCache.Load(dir); ok {
263                 return info, ok
264         }
265         return r.otherCache.Load(dir)
266 }
267
268 func (r *ModuleResolver) cacheStore(info directoryPackageInfo) {
269         if info.rootType == gopathwalk.RootModuleCache {
270                 r.moduleCacheCache.Store(info.dir, info)
271         } else {
272                 r.otherCache.Store(info.dir, info)
273         }
274 }
275
276 func (r *ModuleResolver) cacheKeys() []string {
277         return append(r.moduleCacheCache.Keys(), r.otherCache.Keys()...)
278 }
279
280 // cachePackageName caches the package name for a dir already in the cache.
281 func (r *ModuleResolver) cachePackageName(info directoryPackageInfo) (string, error) {
282         if info.rootType == gopathwalk.RootModuleCache {
283                 return r.moduleCacheCache.CachePackageName(info)
284         }
285         return r.otherCache.CachePackageName(info)
286 }
287
288 func (r *ModuleResolver) cacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) {
289         if info.rootType == gopathwalk.RootModuleCache {
290                 return r.moduleCacheCache.CacheExports(ctx, env, info)
291         }
292         return r.otherCache.CacheExports(ctx, env, info)
293 }
294
295 // findModuleByDir returns the module that contains dir, or nil if no such
296 // module is in scope.
297 func (r *ModuleResolver) findModuleByDir(dir string) *gocommand.ModuleJSON {
298         // This is quite tricky and may not be correct. dir could be:
299         // - a package in the main module.
300         // - a replace target underneath the main module's directory.
301         //    - a nested module in the above.
302         // - a replace target somewhere totally random.
303         //    - a nested module in the above.
304         // - in the mod cache.
305         // - in /vendor/ in -mod=vendor mode.
306         //    - nested module? Dunno.
307         // Rumor has it that replace targets cannot contain other replace targets.
308         for _, m := range r.modsByDir {
309                 if !strings.HasPrefix(dir, m.Dir) {
310                         continue
311                 }
312
313                 if r.dirIsNestedModule(dir, m) {
314                         continue
315                 }
316
317                 return m
318         }
319         return nil
320 }
321
322 // dirIsNestedModule reports if dir is contained in a nested module underneath
323 // mod, not actually in mod.
324 func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON) bool {
325         if !strings.HasPrefix(dir, mod.Dir) {
326                 return false
327         }
328         if r.dirInModuleCache(dir) {
329                 // Nested modules in the module cache are pruned,
330                 // so it cannot be a nested module.
331                 return false
332         }
333         if mod != nil && mod == r.dummyVendorMod {
334                 // The /vendor pseudomodule is flattened and doesn't actually count.
335                 return false
336         }
337         modDir, _ := r.modInfo(dir)
338         if modDir == "" {
339                 return false
340         }
341         return modDir != mod.Dir
342 }
343
344 func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) {
345         readModName := func(modFile string) string {
346                 modBytes, err := ioutil.ReadFile(modFile)
347                 if err != nil {
348                         return ""
349                 }
350                 return modulePath(modBytes)
351         }
352
353         if r.dirInModuleCache(dir) {
354                 if matches := modCacheRegexp.FindStringSubmatch(dir); len(matches) == 3 {
355                         index := strings.Index(dir, matches[1]+"@"+matches[2])
356                         modDir := filepath.Join(dir[:index], matches[1]+"@"+matches[2])
357                         return modDir, readModName(filepath.Join(modDir, "go.mod"))
358                 }
359         }
360         for {
361                 if info, ok := r.cacheLoad(dir); ok {
362                         return info.moduleDir, info.moduleName
363                 }
364                 f := filepath.Join(dir, "go.mod")
365                 info, err := os.Stat(f)
366                 if err == nil && !info.IsDir() {
367                         return dir, readModName(f)
368                 }
369
370                 d := filepath.Dir(dir)
371                 if len(d) >= len(dir) {
372                         return "", "" // reached top of file system, no go.mod
373                 }
374                 dir = d
375         }
376 }
377
378 func (r *ModuleResolver) dirInModuleCache(dir string) bool {
379         if r.moduleCacheDir == "" {
380                 return false
381         }
382         return strings.HasPrefix(dir, r.moduleCacheDir)
383 }
384
385 func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
386         if err := r.init(); err != nil {
387                 return nil, err
388         }
389         names := map[string]string{}
390         for _, path := range importPaths {
391                 _, packageDir := r.findPackage(path)
392                 if packageDir == "" {
393                         continue
394                 }
395                 name, err := packageDirToName(packageDir)
396                 if err != nil {
397                         continue
398                 }
399                 names[path] = name
400         }
401         return names, nil
402 }
403
404 func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error {
405         if err := r.init(); err != nil {
406                 return err
407         }
408
409         processDir := func(info directoryPackageInfo) {
410                 // Skip this directory if we were not able to get the package information successfully.
411                 if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil {
412                         return
413                 }
414                 pkg, err := r.canonicalize(info)
415                 if err != nil {
416                         return
417                 }
418
419                 if !callback.dirFound(pkg) {
420                         return
421                 }
422                 pkg.packageName, err = r.cachePackageName(info)
423                 if err != nil {
424                         return
425                 }
426
427                 if !callback.packageNameLoaded(pkg) {
428                         return
429                 }
430                 _, exports, err := r.loadExports(ctx, pkg, false)
431                 if err != nil {
432                         return
433                 }
434                 callback.exportsLoaded(pkg, exports)
435         }
436
437         // Start processing everything in the cache, and listen for the new stuff
438         // we discover in the walk below.
439         stop1 := r.moduleCacheCache.ScanAndListen(ctx, processDir)
440         defer stop1()
441         stop2 := r.otherCache.ScanAndListen(ctx, processDir)
442         defer stop2()
443
444         // We assume cached directories are fully cached, including all their
445         // children, and have not changed. We can skip them.
446         skip := func(root gopathwalk.Root, dir string) bool {
447                 info, ok := r.cacheLoad(dir)
448                 if !ok {
449                         return false
450                 }
451                 // This directory can be skipped as long as we have already scanned it.
452                 // Packages with errors will continue to have errors, so there is no need
453                 // to rescan them.
454                 packageScanned, _ := info.reachedStatus(directoryScanned)
455                 return packageScanned
456         }
457
458         // Add anything new to the cache, and process it if we're still listening.
459         add := func(root gopathwalk.Root, dir string) {
460                 r.cacheStore(r.scanDirForPackage(root, dir))
461         }
462
463         // r.roots and the callback are not necessarily safe to use in the
464         // goroutine below. Process them eagerly.
465         roots := filterRoots(r.roots, callback.rootFound)
466         // We can't cancel walks, because we need them to finish to have a usable
467         // cache. Instead, run them in a separate goroutine and detach.
468         scanDone := make(chan struct{})
469         go func() {
470                 select {
471                 case <-ctx.Done():
472                         return
473                 case <-r.scanSema:
474                 }
475                 defer func() { r.scanSema <- struct{}{} }()
476                 // We have the lock on r.scannedRoots, and no other scans can run.
477                 for _, root := range roots {
478                         if ctx.Err() != nil {
479                                 return
480                         }
481
482                         if r.scannedRoots[root] {
483                                 continue
484                         }
485                         gopathwalk.WalkSkip([]gopathwalk.Root{root}, add, skip, gopathwalk.Options{Logf: r.env.Logf, ModulesEnabled: true})
486                         r.scannedRoots[root] = true
487                 }
488                 close(scanDone)
489         }()
490         select {
491         case <-ctx.Done():
492         case <-scanDone:
493         }
494         return nil
495 }
496
497 func (r *ModuleResolver) scoreImportPath(ctx context.Context, path string) float64 {
498         if _, ok := stdlib[path]; ok {
499                 return MaxRelevance
500         }
501         mod, _ := r.findPackage(path)
502         return modRelevance(mod)
503 }
504
505 func modRelevance(mod *gocommand.ModuleJSON) float64 {
506         var relevance float64
507         switch {
508         case mod == nil: // out of scope
509                 return MaxRelevance - 4
510         case mod.Indirect:
511                 relevance = MaxRelevance - 3
512         case !mod.Main:
513                 relevance = MaxRelevance - 2
514         default:
515                 relevance = MaxRelevance - 1 // main module ties with stdlib
516         }
517
518         _, versionString, ok := module.SplitPathVersion(mod.Path)
519         if ok {
520                 index := strings.Index(versionString, "v")
521                 if index == -1 {
522                         return relevance
523                 }
524                 if versionNumber, err := strconv.ParseFloat(versionString[index+1:], 64); err == nil {
525                         relevance += versionNumber / 1000
526                 }
527         }
528
529         return relevance
530 }
531
532 // canonicalize gets the result of canonicalizing the packages using the results
533 // of initializing the resolver from 'go list -m'.
534 func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) {
535         // Packages in GOROOT are already canonical, regardless of the std/cmd modules.
536         if info.rootType == gopathwalk.RootGOROOT {
537                 return &pkg{
538                         importPathShort: info.nonCanonicalImportPath,
539                         dir:             info.dir,
540                         packageName:     path.Base(info.nonCanonicalImportPath),
541                         relevance:       MaxRelevance,
542                 }, nil
543         }
544
545         importPath := info.nonCanonicalImportPath
546         mod := r.findModuleByDir(info.dir)
547         // Check if the directory is underneath a module that's in scope.
548         if mod != nil {
549                 // It is. If dir is the target of a replace directive,
550                 // our guessed import path is wrong. Use the real one.
551                 if mod.Dir == info.dir {
552                         importPath = mod.Path
553                 } else {
554                         dirInMod := info.dir[len(mod.Dir)+len("/"):]
555                         importPath = path.Join(mod.Path, filepath.ToSlash(dirInMod))
556                 }
557         } else if !strings.HasPrefix(importPath, info.moduleName) {
558                 // The module's name doesn't match the package's import path. It
559                 // probably needs a replace directive we don't have.
560                 return nil, fmt.Errorf("package in %q is not valid without a replace statement", info.dir)
561         }
562
563         res := &pkg{
564                 importPathShort: importPath,
565                 dir:             info.dir,
566                 relevance:       modRelevance(mod),
567         }
568         // We may have discovered a package that has a different version
569         // in scope already. Canonicalize to that one if possible.
570         if _, canonicalDir := r.findPackage(importPath); canonicalDir != "" {
571                 res.dir = canonicalDir
572         }
573         return res, nil
574 }
575
576 func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) {
577         if err := r.init(); err != nil {
578                 return "", nil, err
579         }
580         if info, ok := r.cacheLoad(pkg.dir); ok && !includeTest {
581                 return r.cacheExports(ctx, r.env, info)
582         }
583         return loadExportsFromFiles(ctx, r.env, pkg.dir, includeTest)
584 }
585
586 func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) directoryPackageInfo {
587         subdir := ""
588         if dir != root.Path {
589                 subdir = dir[len(root.Path)+len("/"):]
590         }
591         importPath := filepath.ToSlash(subdir)
592         if strings.HasPrefix(importPath, "vendor/") {
593                 // Only enter vendor directories if they're explicitly requested as a root.
594                 return directoryPackageInfo{
595                         status: directoryScanned,
596                         err:    fmt.Errorf("unwanted vendor directory"),
597                 }
598         }
599         switch root.Type {
600         case gopathwalk.RootCurrentModule:
601                 importPath = path.Join(r.main.Path, filepath.ToSlash(subdir))
602         case gopathwalk.RootModuleCache:
603                 matches := modCacheRegexp.FindStringSubmatch(subdir)
604                 if len(matches) == 0 {
605                         return directoryPackageInfo{
606                                 status: directoryScanned,
607                                 err:    fmt.Errorf("invalid module cache path: %v", subdir),
608                         }
609                 }
610                 modPath, err := module.UnescapePath(filepath.ToSlash(matches[1]))
611                 if err != nil {
612                         if r.env.Logf != nil {
613                                 r.env.Logf("decoding module cache path %q: %v", subdir, err)
614                         }
615                         return directoryPackageInfo{
616                                 status: directoryScanned,
617                                 err:    fmt.Errorf("decoding module cache path %q: %v", subdir, err),
618                         }
619                 }
620                 importPath = path.Join(modPath, filepath.ToSlash(matches[3]))
621         }
622
623         modDir, modName := r.modInfo(dir)
624         result := directoryPackageInfo{
625                 status:                 directoryScanned,
626                 dir:                    dir,
627                 rootType:               root.Type,
628                 nonCanonicalImportPath: importPath,
629                 moduleDir:              modDir,
630                 moduleName:             modName,
631         }
632         if root.Type == gopathwalk.RootGOROOT {
633                 // stdlib packages are always in scope, despite the confusing go.mod
634                 return result
635         }
636         return result
637 }
638
639 // modCacheRegexp splits a path in a module cache into module, module version, and package.
640 var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
641
642 var (
643         slashSlash = []byte("//")
644         moduleStr  = []byte("module")
645 )
646
647 // modulePath returns the module path from the gomod file text.
648 // If it cannot find a module path, it returns an empty string.
649 // It is tolerant of unrelated problems in the go.mod file.
650 //
651 // Copied from cmd/go/internal/modfile.
652 func modulePath(mod []byte) string {
653         for len(mod) > 0 {
654                 line := mod
655                 mod = nil
656                 if i := bytes.IndexByte(line, '\n'); i >= 0 {
657                         line, mod = line[:i], line[i+1:]
658                 }
659                 if i := bytes.Index(line, slashSlash); i >= 0 {
660                         line = line[:i]
661                 }
662                 line = bytes.TrimSpace(line)
663                 if !bytes.HasPrefix(line, moduleStr) {
664                         continue
665                 }
666                 line = line[len(moduleStr):]
667                 n := len(line)
668                 line = bytes.TrimSpace(line)
669                 if len(line) == n || len(line) == 0 {
670                         continue
671                 }
672
673                 if line[0] == '"' || line[0] == '`' {
674                         p, err := strconv.Unquote(string(line))
675                         if err != nil {
676                                 return "" // malformed quoted string or multiline module path
677                         }
678                         return p
679                 }
680
681                 return string(line)
682         }
683         return "" // missing module path
684 }