Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / cache / check.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/cache/check.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/cache/check.go
new file mode 100644 (file)
index 0000000..187a391
--- /dev/null
@@ -0,0 +1,548 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cache
+
+import (
+       "bytes"
+       "context"
+       "fmt"
+       "go/ast"
+       "go/types"
+       "path"
+       "sort"
+       "strings"
+       "sync"
+
+       "golang.org/x/mod/module"
+       "golang.org/x/tools/go/packages"
+       "golang.org/x/tools/internal/event"
+       "golang.org/x/tools/internal/lsp/debug/tag"
+       "golang.org/x/tools/internal/lsp/source"
+       "golang.org/x/tools/internal/memoize"
+       "golang.org/x/tools/internal/span"
+       "golang.org/x/tools/internal/typesinternal"
+       errors "golang.org/x/xerrors"
+)
+
+type packageHandleKey string
+
+type packageHandle struct {
+       handle *memoize.Handle
+
+       goFiles, compiledGoFiles []*parseGoHandle
+
+       // mode is the mode the files were parsed in.
+       mode source.ParseMode
+
+       // m is the metadata associated with the package.
+       m *metadata
+
+       // key is the hashed key for the package.
+       key packageHandleKey
+}
+
+func (ph *packageHandle) packageKey() packageKey {
+       return packageKey{
+               id:   ph.m.id,
+               mode: ph.mode,
+       }
+}
+
+// packageData contains the data produced by type-checking a package.
+type packageData struct {
+       pkg *pkg
+       err error
+}
+
+// buildPackageHandle returns a packageHandle for a given package and mode.
+func (s *snapshot) buildPackageHandle(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, error) {
+       if ph := s.getPackage(id, mode); ph != nil {
+               return ph, nil
+       }
+
+       // Build the packageHandle for this ID and its dependencies.
+       ph, deps, err := s.buildKey(ctx, id, mode)
+       if err != nil {
+               return nil, err
+       }
+
+       // Do not close over the packageHandle or the snapshot in the Bind function.
+       // This creates a cycle, which causes the finalizers to never run on the handles.
+       // The possible cycles are:
+       //
+       //     packageHandle.h.function -> packageHandle
+       //     packageHandle.h.function -> snapshot -> packageHandle
+       //
+
+       m := ph.m
+       key := ph.key
+
+       h := s.generation.Bind(key, func(ctx context.Context, arg memoize.Arg) interface{} {
+               snapshot := arg.(*snapshot)
+
+               // Begin loading the direct dependencies, in parallel.
+               var wg sync.WaitGroup
+               for _, dep := range deps {
+                       wg.Add(1)
+                       go func(dep *packageHandle) {
+                               dep.check(ctx, snapshot)
+                               wg.Done()
+                       }(dep)
+               }
+
+               data := &packageData{}
+               data.pkg, data.err = typeCheck(ctx, snapshot, m, mode, deps)
+               // Make sure that the workers above have finished before we return,
+               // especially in case of cancellation.
+               wg.Wait()
+
+               return data
+       }, nil)
+       ph.handle = h
+
+       // Cache the handle in the snapshot. If a package handle has already
+       // been cached, addPackage will return the cached value. This is fine,
+       // since the original package handle above will have no references and be
+       // garbage collected.
+       ph = s.addPackageHandle(ph)
+
+       return ph, nil
+}
+
+// buildKey computes the key for a given packageHandle.
+func (s *snapshot) buildKey(ctx context.Context, id packageID, mode source.ParseMode) (*packageHandle, map[packagePath]*packageHandle, error) {
+       m := s.getMetadata(id)
+       if m == nil {
+               return nil, nil, errors.Errorf("no metadata for %s", id)
+       }
+       goFiles, err := s.parseGoHandles(ctx, m.goFiles, mode)
+       if err != nil {
+               return nil, nil, err
+       }
+       compiledGoFiles, err := s.parseGoHandles(ctx, m.compiledGoFiles, mode)
+       if err != nil {
+               return nil, nil, err
+       }
+       ph := &packageHandle{
+               m:               m,
+               goFiles:         goFiles,
+               compiledGoFiles: compiledGoFiles,
+               mode:            mode,
+       }
+       // Make sure all of the depList are sorted.
+       depList := append([]packageID{}, m.deps...)
+       sort.Slice(depList, func(i, j int) bool {
+               return depList[i] < depList[j]
+       })
+
+       deps := make(map[packagePath]*packageHandle)
+
+       // Begin computing the key by getting the depKeys for all dependencies.
+       var depKeys []packageHandleKey
+       for _, depID := range depList {
+               depHandle, err := s.buildPackageHandle(ctx, depID, s.workspaceParseMode(depID))
+               if err != nil {
+                       event.Error(ctx, fmt.Sprintf("%s: no dep handle for %s", id, depID), err, tag.Snapshot.Of(s.id))
+                       if ctx.Err() != nil {
+                               return nil, nil, ctx.Err()
+                       }
+                       // One bad dependency should not prevent us from checking the entire package.
+                       // Add a special key to mark a bad dependency.
+                       depKeys = append(depKeys, packageHandleKey(fmt.Sprintf("%s import not found", id)))
+                       continue
+               }
+               deps[depHandle.m.pkgPath] = depHandle
+               depKeys = append(depKeys, depHandle.key)
+       }
+       experimentalKey := s.View().Options().ExperimentalPackageCacheKey
+       ph.key = checkPackageKey(ctx, ph.m.id, compiledGoFiles, m.config, depKeys, mode, experimentalKey)
+       return ph, deps, nil
+}
+
+func (s *snapshot) workspaceParseMode(id packageID) source.ParseMode {
+       if _, ws := s.isWorkspacePackage(id); ws {
+               return source.ParseFull
+       } else {
+               return source.ParseExported
+       }
+}
+
+func checkPackageKey(ctx context.Context, id packageID, pghs []*parseGoHandle, cfg *packages.Config, deps []packageHandleKey, mode source.ParseMode, experimentalKey bool) packageHandleKey {
+       b := bytes.NewBuffer(nil)
+       b.WriteString(string(id))
+       if !experimentalKey {
+               // cfg was used to produce the other hashed inputs (package ID, parsed Go
+               // files, and deps). It should not otherwise affect the inputs to the type
+               // checker, so this experiment omits it. This should increase cache hits on
+               // the daemon as cfg contains the environment and working directory.
+               b.WriteString(hashConfig(cfg))
+       }
+       b.WriteByte(byte(mode))
+       for _, dep := range deps {
+               b.WriteString(string(dep))
+       }
+       for _, cgf := range pghs {
+               b.WriteString(cgf.file.FileIdentity().String())
+       }
+       return packageHandleKey(hashContents(b.Bytes()))
+}
+
+// hashEnv returns a hash of the snapshot's configuration.
+func hashEnv(s *snapshot) string {
+       s.view.optionsMu.Lock()
+       env := s.view.options.EnvSlice()
+       s.view.optionsMu.Unlock()
+
+       b := &bytes.Buffer{}
+       for _, e := range env {
+               b.WriteString(e)
+       }
+       return hashContents(b.Bytes())
+}
+
+// hashConfig returns the hash for the *packages.Config.
+func hashConfig(config *packages.Config) string {
+       b := bytes.NewBuffer(nil)
+
+       // Dir, Mode, Env, BuildFlags are the parts of the config that can change.
+       b.WriteString(config.Dir)
+       b.WriteString(string(rune(config.Mode)))
+
+       for _, e := range config.Env {
+               b.WriteString(e)
+       }
+       for _, f := range config.BuildFlags {
+               b.WriteString(f)
+       }
+       return hashContents(b.Bytes())
+}
+
+func (ph *packageHandle) Check(ctx context.Context, s source.Snapshot) (source.Package, error) {
+       return ph.check(ctx, s.(*snapshot))
+}
+
+func (ph *packageHandle) check(ctx context.Context, s *snapshot) (*pkg, error) {
+       v, err := ph.handle.Get(ctx, s.generation, s)
+       if err != nil {
+               return nil, err
+       }
+       data := v.(*packageData)
+       return data.pkg, data.err
+}
+
+func (ph *packageHandle) CompiledGoFiles() []span.URI {
+       return ph.m.compiledGoFiles
+}
+
+func (ph *packageHandle) ID() string {
+       return string(ph.m.id)
+}
+
+func (ph *packageHandle) cached(g *memoize.Generation) (*pkg, error) {
+       v := ph.handle.Cached(g)
+       if v == nil {
+               return nil, errors.Errorf("no cached type information for %s", ph.m.pkgPath)
+       }
+       data := v.(*packageData)
+       return data.pkg, data.err
+}
+
+func (s *snapshot) parseGoHandles(ctx context.Context, files []span.URI, mode source.ParseMode) ([]*parseGoHandle, error) {
+       pghs := make([]*parseGoHandle, 0, len(files))
+       for _, uri := range files {
+               fh, err := s.GetFile(ctx, uri)
+               if err != nil {
+                       return nil, err
+               }
+               pghs = append(pghs, s.parseGoHandle(ctx, fh, mode))
+       }
+       return pghs, nil
+}
+
+func typeCheck(ctx context.Context, snapshot *snapshot, m *metadata, mode source.ParseMode, deps map[packagePath]*packageHandle) (*pkg, error) {
+       ctx, done := event.Start(ctx, "cache.importer.typeCheck", tag.Package.Of(string(m.id)))
+       defer done()
+
+       var rawErrors []error
+       for _, err := range m.errors {
+               rawErrors = append(rawErrors, err)
+       }
+
+       fset := snapshot.view.session.cache.fset
+       pkg := &pkg{
+               m:               m,
+               mode:            mode,
+               goFiles:         make([]*source.ParsedGoFile, len(m.goFiles)),
+               compiledGoFiles: make([]*source.ParsedGoFile, len(m.compiledGoFiles)),
+               imports:         make(map[packagePath]*pkg),
+               typesSizes:      m.typesSizes,
+               typesInfo: &types.Info{
+                       Types:      make(map[ast.Expr]types.TypeAndValue),
+                       Defs:       make(map[*ast.Ident]types.Object),
+                       Uses:       make(map[*ast.Ident]types.Object),
+                       Implicits:  make(map[ast.Node]types.Object),
+                       Selections: make(map[*ast.SelectorExpr]*types.Selection),
+                       Scopes:     make(map[ast.Node]*types.Scope),
+               },
+       }
+       // If this is a replaced module in the workspace, the version is
+       // meaningless, and we don't want clients to access it.
+       if m.module != nil {
+               version := m.module.Version
+               if source.IsWorkspaceModuleVersion(version) {
+                       version = ""
+               }
+               pkg.version = &module.Version{
+                       Path:    m.module.Path,
+                       Version: version,
+               }
+       }
+       var (
+               files        = make([]*ast.File, len(m.compiledGoFiles))
+               parseErrors  = make([]error, len(m.compiledGoFiles))
+               actualErrors = make([]error, len(m.compiledGoFiles))
+               wg           sync.WaitGroup
+
+               mu             sync.Mutex
+               skipTypeErrors bool
+       )
+       for i, cgf := range m.compiledGoFiles {
+               wg.Add(1)
+               go func(i int, cgf span.URI) {
+                       defer wg.Done()
+                       fh, err := snapshot.GetFile(ctx, cgf)
+                       if err != nil {
+                               actualErrors[i] = err
+                               return
+                       }
+                       pgh := snapshot.parseGoHandle(ctx, fh, mode)
+                       pgf, fixed, err := snapshot.parseGo(ctx, pgh)
+                       if err != nil {
+                               actualErrors[i] = err
+                               return
+                       }
+                       pkg.compiledGoFiles[i] = pgf
+                       files[i], parseErrors[i], actualErrors[i] = pgf.File, pgf.ParseErr, err
+
+                       mu.Lock()
+                       skipTypeErrors = skipTypeErrors || fixed
+                       mu.Unlock()
+               }(i, cgf)
+       }
+       for i, gf := range m.goFiles {
+               wg.Add(1)
+               // We need to parse the non-compiled go files, but we don't care about their errors.
+               go func(i int, gf span.URI) {
+                       defer wg.Done()
+                       fh, err := snapshot.GetFile(ctx, gf)
+                       if err != nil {
+                               return
+                       }
+                       pgf, _ := snapshot.ParseGo(ctx, fh, mode)
+                       pkg.goFiles[i] = pgf
+               }(i, gf)
+       }
+       wg.Wait()
+       for _, err := range actualErrors {
+               if err != nil {
+                       return nil, err
+               }
+       }
+
+       for _, e := range parseErrors {
+               if e != nil {
+                       rawErrors = append(rawErrors, e)
+               }
+       }
+
+       var i int
+       for _, f := range files {
+               if f != nil {
+                       files[i] = f
+                       i++
+               }
+       }
+       files = files[:i]
+
+       // Use the default type information for the unsafe package.
+       if pkg.m.pkgPath == "unsafe" {
+               pkg.types = types.Unsafe
+               // Don't type check Unsafe: it's unnecessary, and doing so exposes a data
+               // race to Unsafe.completed.
+               return pkg, nil
+       } else if len(files) == 0 { // not the unsafe package, no parsed files
+               // Try to attach errors messages to the file as much as possible.
+               var found bool
+               for _, e := range rawErrors {
+                       srcErr, err := sourceError(ctx, snapshot, pkg, e)
+                       if err != nil {
+                               continue
+                       }
+                       found = true
+                       pkg.errors = append(pkg.errors, srcErr)
+               }
+               if found {
+                       return pkg, nil
+               }
+               return nil, errors.Errorf("no parsed files for package %s, expected: %v, list errors: %v", pkg.m.pkgPath, pkg.compiledGoFiles, rawErrors)
+       } else {
+               pkg.types = types.NewPackage(string(m.pkgPath), string(m.name))
+       }
+
+       cfg := &types.Config{
+               Error: func(e error) {
+                       // If we have fixed parse errors in any of the files,
+                       // we should hide type errors, as they may be completely nonsensical.
+                       if skipTypeErrors {
+                               return
+                       }
+                       rawErrors = append(rawErrors, e)
+               },
+               Importer: importerFunc(func(pkgPath string) (*types.Package, error) {
+                       // If the context was cancelled, we should abort.
+                       if ctx.Err() != nil {
+                               return nil, ctx.Err()
+                       }
+                       dep := resolveImportPath(pkgPath, pkg, deps)
+                       if dep == nil {
+                               return nil, errors.Errorf("no package for import %s", pkgPath)
+                       }
+                       if !isValidImport(m.pkgPath, dep.m.pkgPath) {
+                               return nil, errors.Errorf("invalid use of internal package %s", pkgPath)
+                       }
+                       depPkg, err := dep.check(ctx, snapshot)
+                       if err != nil {
+                               return nil, err
+                       }
+                       pkg.imports[depPkg.m.pkgPath] = depPkg
+                       return depPkg.types, nil
+               }),
+       }
+       // We want to type check cgo code if go/types supports it.
+       // We passed typecheckCgo to go/packages when we Loaded.
+       typesinternal.SetUsesCgo(cfg)
+
+       check := types.NewChecker(cfg, fset, pkg.types, pkg.typesInfo)
+
+       // Type checking errors are handled via the config, so ignore them here.
+       _ = check.Files(files)
+       // If the context was cancelled, we may have returned a ton of transient
+       // errors to the type checker. Swallow them.
+       if ctx.Err() != nil {
+               return nil, ctx.Err()
+       }
+
+       // We don't care about a package's errors unless we have parsed it in full.
+       if mode == source.ParseFull {
+               expandErrors(rawErrors)
+               for _, e := range rawErrors {
+                       srcErr, err := sourceError(ctx, snapshot, pkg, e)
+                       if err != nil {
+                               event.Error(ctx, "unable to compute error positions", err, tag.Package.Of(pkg.ID()))
+                               continue
+                       }
+                       pkg.errors = append(pkg.errors, srcErr)
+                       if err, ok := e.(extendedError); ok {
+                               pkg.typeErrors = append(pkg.typeErrors, err.primary)
+                       }
+               }
+       }
+
+       return pkg, nil
+}
+
+type extendedError struct {
+       primary     types.Error
+       secondaries []types.Error
+}
+
+func (e extendedError) Error() string {
+       return e.primary.Error()
+}
+
+// expandErrors duplicates "secondary" errors by mapping them to their main
+// error. Some errors returned by the type checker are followed by secondary
+// errors which give more information about the error. These are errors in
+// their own right, and they are marked by starting with \t. For instance, when
+// there is a multiply-defined function, the secondary error points back to the
+// definition first noticed.
+//
+// This code associates the secondary error with its primary error, which can
+// then be used as RelatedInformation when the error becomes a diagnostic.
+func expandErrors(errs []error) []error {
+       for i := 0; i < len(errs); {
+               e, ok := errs[i].(types.Error)
+               if !ok {
+                       i++
+                       continue
+               }
+               enew := extendedError{
+                       primary: e,
+               }
+               j := i + 1
+               for ; j < len(errs); j++ {
+                       spl, ok := errs[j].(types.Error)
+                       if !ok || len(spl.Msg) == 0 || spl.Msg[0] != '\t' {
+                               break
+                       }
+                       enew.secondaries = append(enew.secondaries, spl)
+               }
+               errs[i] = enew
+               i = j
+       }
+       return errs
+}
+
+// resolveImportPath resolves an import path in pkg to a package from deps.
+// It should produce the same results as resolveImportPath:
+// https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/load/pkg.go;drc=641918ee09cb44d282a30ee8b66f99a0b63eaef9;l=990.
+func resolveImportPath(importPath string, pkg *pkg, deps map[packagePath]*packageHandle) *packageHandle {
+       if dep := deps[packagePath(importPath)]; dep != nil {
+               return dep
+       }
+       // We may be in GOPATH mode, in which case we need to check vendor dirs.
+       searchDir := path.Dir(pkg.PkgPath())
+       for {
+               vdir := packagePath(path.Join(searchDir, "vendor", importPath))
+               if vdep := deps[vdir]; vdep != nil {
+                       return vdep
+               }
+
+               // Search until Dir doesn't take us anywhere new, e.g. "." or "/".
+               next := path.Dir(searchDir)
+               if searchDir == next {
+                       break
+               }
+               searchDir = next
+       }
+
+       // Vendor didn't work. Let's try minimal module compatibility mode.
+       // In MMC, the packagePath is the canonical (.../vN/...) path, which
+       // is hard to calculate. But the go command has already resolved the ID
+       // to the non-versioned path, and we can take advantage of that.
+       for _, dep := range deps {
+               if dep.ID() == importPath {
+                       return dep
+               }
+       }
+       return nil
+}
+
+func isValidImport(pkgPath, importPkgPath packagePath) bool {
+       i := strings.LastIndex(string(importPkgPath), "/internal/")
+       if i == -1 {
+               return true
+       }
+       if pkgPath == "command-line-arguments" {
+               return true
+       }
+       return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i]))
+}
+
+// An importFunc is an implementation of the single-method
+// types.Importer interface based on a function value.
+type importerFunc func(path string) (*types.Package, error)
+
+func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }