func (s *snapshot) transitiveReverseDependencies(id PackageID, ids map[PackageID]struct{}) {
	if _, ok := ids[id]; ok {
		return
	}
	m := s.getMetadata(id)
	if m == nil || !(m.Valid || s.useInvalidMetadata()) {
		return
	}
	ids[id] = struct{}{}
	importedBy := s.getImportedBy(id)
	for _, parentID := range importedBy {
		s.transitiveReverseDependencies(parentID, ids)
	}
}
transitiveReverseDependencies populates the ids map with package IDs
belonging to the provided package and its transitive reverse dependencies.
package cache
import (
	"bytes"
	"context"
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"golang.org/x/mod/modfile"
	"golang.org/x/mod/module"
	"golang.org/x/mod/semver"
	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/packages"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/event"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/gocommand"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/debug/log"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/debug/tag"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/lsp/source"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/memoize"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/packagesinternal"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/span"
	"github.com/govim/govim/cmd/govim/internal/golang_org_x_tools/typesinternal"
	errors "golang.org/x/xerrors"
)
type snapshot struct {
	memoize.Arg  
	id   uint64
	view *View
	cancel        func()
	backgroundCtx context.Context
	generation *memoize.Generation
	initializeOnce *sync.Once
	initializedErr *source.CriticalError
	mu sync.Mutex
	builtin span.URI
	ids map[span.URI][]PackageID
	metadata map[PackageID]*KnownMetadata
	importedBy map[PackageID][]PackageID
	files map[span.URI]source.VersionedFileHandle
	goFiles map[parseKey]*parseGoHandle
	symbols map[span.URI]*symbolHandle
	packages map[packageKey]*packageHandle
	actions map[actionKey]*actionHandle
	workspacePackages map[PackageID]PackagePath
	unloadableFiles map[span.URI]struct{}
	parseModHandles map[span.URI]*parseModHandle
	modTidyHandles map[span.URI]*modTidyHandle
	modWhyHandles  map[span.URI]*modWhyHandle
	workspace          *workspace
	workspaceDirHandle *memoize.Handle
	knownSubdirs map[span.URI]struct{}
	unprocessedSubdirChanges []*fileChange
}
type packageKey struct {
	mode source.ParseMode
	id   PackageID
}
type actionKey struct {
	pkg      packageKey
	analyzer *analysis.Analyzer
}
func (s *snapshot) ID() uint64 {
	return s.id
}
func (s *snapshot) View() source.View {
	return s.view
}
func (s *snapshot) BackgroundContext() context.Context {
	return s.backgroundCtx
}
func (s *snapshot) FileSet() *token.FileSet {
	return s.view.session.cache.fset
}
func (s *snapshot) ModFiles() []span.URI {
	var uris []span.URI
	for modURI := range s.workspace.getActiveModFiles() {
		uris = append(uris, modURI)
	}
	return uris
}
func (s *snapshot) Templates() map[span.URI]source.VersionedFileHandle {
	if !s.view.Options().ExperimentalTemplateSupport {
		return nil
	}
	ans := map[span.URI]source.VersionedFileHandle{}
	s.mu.Lock()
	defer s.mu.Unlock()
	for k, x := range s.files {
		if strings.HasSuffix(filepath.Ext(k.Filename()), "tmpl") {
			ans[k] = x
		}
	}
	return ans
}
func (s *snapshot) ValidBuildConfiguration() bool {
	return validBuildConfiguration(s.view.rootURI, &s.view.workspaceInformation, s.workspace.getActiveModFiles())
}
func (s *snapshot) workspaceMode() workspaceMode {
	var mode workspaceMode
	validBuildConfiguration := s.ValidBuildConfiguration()
	if !validBuildConfiguration {
		return mode
	}
	if len(s.workspace.getActiveModFiles()) == 0 && validBuildConfiguration {
		return mode
	}
	mode |= moduleMode
	options := s.view.Options()
	if options.TempModfile && s.view.workspaceInformation.goversion >= 14 {
		mode |= tempModfile
	}
	if !options.ExpandWorkspaceToModule {
		return mode
	}
	if s.workspace.moduleSource != goWorkWorkspace && s.workspace.moduleSource != goplsModWorkspace && !options.ExperimentalWorkspaceModule {
		return mode
	}
	mode |= usesWorkspaceModule
	return mode
}
func (s *snapshot) config(ctx context.Context, inv *gocommand.Invocation) *packages.Config {
	s.view.optionsMu.Lock()
	verboseOutput := s.view.options.VerboseOutput
	s.view.optionsMu.Unlock()
	cfg := &packages.Config{
		Context:    ctx,
		Dir:        inv.WorkingDir,
		Env:        inv.Env,
		BuildFlags: inv.BuildFlags,
		Mode: packages.NeedName |
			packages.NeedFiles |
			packages.NeedCompiledGoFiles |
			packages.NeedImports |
			packages.NeedDeps |
			packages.NeedTypesSizes |
			packages.NeedModule,
		Fset:    s.FileSet(),
		Overlay: s.buildOverlay(),
		ParseFile: func(*token.FileSet, string, []byte) (*ast.File, error) {
			panic("go/packages must not be used to parse files")
		},
		Logf: func(format string, args ...interface{}) {
			if verboseOutput {
				event.Log(ctx, fmt.Sprintf(format, args...))
			}
		},
		Tests: true,
	}
	packagesinternal.SetModFile(cfg, inv.ModFile)
	packagesinternal.SetModFlag(cfg, inv.ModFlag)
	if typesinternal.SetUsesCgo(&types.Config{}) {
		cfg.Mode |= packages.LoadMode(packagesinternal.TypecheckCgo)
	}
	packagesinternal.SetGoCmdRunner(cfg, s.view.session.gocmdRunner)
	return cfg
}
func (s *snapshot) RunGoCommandDirect(ctx context.Context, mode source.InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error) {
	_, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
	if err != nil {
		return nil, err
	}
	defer cleanup()
	return s.view.session.gocmdRunner.Run(ctx, *inv)
}
func (s *snapshot) RunGoCommandPiped(ctx context.Context, mode source.InvocationFlags, inv *gocommand.Invocation, stdout, stderr io.Writer) error {
	_, inv, cleanup, err := s.goCommandInvocation(ctx, mode, inv)
	if err != nil {
		return err
	}
	defer cleanup()
	return s.view.session.gocmdRunner.RunPiped(ctx, *inv, stdout, stderr)
}
func (s *snapshot) RunGoCommands(ctx context.Context, allowNetwork bool, wd string, run func(invoke func(...string) (*bytes.Buffer, error)) error) (bool, []byte, []byte, error) {
	var flags source.InvocationFlags
	if s.workspaceMode()&tempModfile != 0 {
		flags = source.WriteTemporaryModFile
	} else {
		flags = source.Normal
	}
	if allowNetwork {
		flags |= source.AllowNetwork
	}
	tmpURI, inv, cleanup, err := s.goCommandInvocation(ctx, flags, &gocommand.Invocation{WorkingDir: wd})
	if err != nil {
		return false, nil, nil, err
	}
	defer cleanup()
	invoke := func(args ...string) (*bytes.Buffer, error) {
		inv.Verb = args[0]
		inv.Args = args[1:]
		return s.view.session.gocmdRunner.Run(ctx, *inv)
	}
	if err := run(invoke); err != nil {
		return false, nil, nil, err
	}
	if flags.Mode() != source.WriteTemporaryModFile {
		return false, nil, nil, nil
	}
	var modBytes, sumBytes []byte
	modBytes, err = ioutil.ReadFile(tmpURI.Filename())
	if err != nil && !os.IsNotExist(err) {
		return false, nil, nil, err
	}
	sumBytes, err = ioutil.ReadFile(strings.TrimSuffix(tmpURI.Filename(), ".mod") + ".sum")
	if err != nil && !os.IsNotExist(err) {
		return false, nil, nil, err
	}
	return true, modBytes, sumBytes, nil
}
func (s *snapshot) goCommandInvocation(ctx context.Context, flags source.InvocationFlags, inv *gocommand.Invocation) (tmpURI span.URI, updatedInv *gocommand.Invocation, cleanup func(), err error) {
	s.view.optionsMu.Lock()
	allowModfileModificationOption := s.view.options.AllowModfileModifications
	allowNetworkOption := s.view.options.AllowImplicitNetworkAccess
	inv.Env = append(append(append(os.Environ(), s.view.options.EnvSlice()...), inv.Env...), "GO111MODULE="+s.view.effectiveGo111Module)
	inv.BuildFlags = append([]string{}, s.view.options.BuildFlags...)
	s.view.optionsMu.Unlock()
	cleanup = func() {}  
	if s.workspaceMode()&moduleMode == 0 {
		return "", inv, cleanup, nil
	}
	mode, allowNetwork := flags.Mode(), flags.AllowNetwork()
	if !allowNetwork && !allowNetworkOption {
		inv.Env = append(inv.Env, "GOPROXY=off")
	}
	var modURI span.URI
	if mode == source.LoadWorkspace {
		if s.workspaceMode()&usesWorkspaceModule == 0 {
			for m := range s.workspace.getActiveModFiles() {  
				modURI = m
			}
		} else {
			var tmpDir span.URI
			var err error
			tmpDir, err = s.getWorkspaceDir(ctx)
			if err != nil {
				return "", nil, cleanup, err
			}
			inv.WorkingDir = tmpDir.Filename()
			modURI = span.URIFromPath(filepath.Join(tmpDir.Filename(), "go.mod"))
		}
	} else {
		modURI = s.GoModForFile(span.URIFromPath(inv.WorkingDir))
	}
	var modContent []byte
	if modURI != "" {
		modFH, err := s.GetFile(ctx, modURI)
		if err != nil {
			return "", nil, cleanup, err
		}
		modContent, err = modFH.Read()
		if err != nil {
			return "", nil, cleanup, err
		}
	}
	vendorEnabled, err := s.vendorEnabled(ctx, modURI, modContent)
	if err != nil {
		return "", nil, cleanup, err
	}
	if inv.ModFlag == "" {
		mutableModFlag := ""
		if s.view.goversion >= 16 {
			mutableModFlag = "mod"
		}
		switch mode {
		case source.LoadWorkspace, source.Normal:
			if vendorEnabled {
				inv.ModFlag = "vendor"
			} else if !allowModfileModificationOption {
				inv.ModFlag = "readonly"
			} else {
				inv.ModFlag = mutableModFlag
			}
		case source.UpdateUserModFile, source.WriteTemporaryModFile:
			inv.ModFlag = mutableModFlag
		}
	}
	wantTempMod := mode != source.UpdateUserModFile
	needTempMod := mode == source.WriteTemporaryModFile
	tempMod := wantTempMod && s.workspaceMode()&tempModfile != 0
	if needTempMod && !tempMod {
		return "", nil, cleanup, source.ErrTmpModfileUnsupported
	}
	if tempMod {
		if modURI == "" {
			return "", nil, cleanup, fmt.Errorf("no go.mod file found in %s", inv.WorkingDir)
		}
		modFH, err := s.GetFile(ctx, modURI)
		if err != nil {
			return "", nil, cleanup, err
		}
		gosum := s.goSum(ctx, modURI)
		tmpURI, cleanup, err = tempModFile(modFH, gosum)
		if err != nil {
			return "", nil, cleanup, err
		}
		inv.ModFile = tmpURI.Filename()
	}
	return tmpURI, inv, cleanup, nil
}
func (s *snapshot) buildOverlay() map[string][]byte {
	s.mu.Lock()
	defer s.mu.Unlock()
	overlays := make(map[string][]byte)
	for uri, fh := range s.files {
		overlay, ok := fh.(*overlay)
		if !ok {
			continue
		}
		if overlay.saved {
			continue
		}
		overlays[uri.Filename()] = overlay.text
	}
	return overlays
}
func hashUnsavedOverlays(files map[span.URI]source.VersionedFileHandle) string {
	var unsaved []string
	for uri, fh := range files {
		if overlay, ok := fh.(*overlay); ok && !overlay.saved {
			unsaved = append(unsaved, uri.Filename())
		}
	}
	sort.Strings(unsaved)
	return hashContents([]byte(strings.Join(unsaved, "")))
}
func (s *snapshot) PackagesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode, includeTestVariants bool) ([]source.Package, error) {
	ctx = event.Label(ctx, tag.URI.Of(uri))
	phs, err := s.packageHandlesForFile(ctx, uri, mode, includeTestVariants)
	if err != nil {
		return nil, err
	}
	var pkgs []source.Package
	for _, ph := range phs {
		pkg, err := ph.check(ctx, s)
		if err != nil {
			return nil, err
		}
		pkgs = append(pkgs, pkg)
	}
	return pkgs, nil
}
func (s *snapshot) PackageForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode, pkgPolicy source.PackageFilter) (source.Package, error) {
	ctx = event.Label(ctx, tag.URI.Of(uri))
	phs, err := s.packageHandlesForFile(ctx, uri, mode, false)
	if err != nil {
		return nil, err
	}
	if len(phs) < 1 {
		return nil, errors.Errorf("no packages")
	}
	ph := phs[0]
	for _, handle := range phs[1:] {
		switch pkgPolicy {
		case source.WidestPackage:
			if ph == nil || len(handle.CompiledGoFiles()) > len(ph.CompiledGoFiles()) {
				ph = handle
			}
		case source.NarrowestPackage:
			if ph == nil || len(handle.CompiledGoFiles()) < len(ph.CompiledGoFiles()) {
				ph = handle
			}
		}
	}
	if ph == nil {
		return nil, errors.Errorf("no packages in input")
	}
	return ph.check(ctx, s)
}
func (s *snapshot) packageHandlesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode, includeTestVariants bool) ([]*packageHandle, error) {
	fh, err := s.GetFile(ctx, uri)
	if err != nil {
		return nil, err
	}
	if fh.Kind() != source.Go {
		return nil, fmt.Errorf("no packages for non-Go file %s", uri)
	}
	knownIDs, err := s.getOrLoadIDsForURI(ctx, uri)
	if err != nil {
		return nil, err
	}
	var phs []*packageHandle
	for _, id := range knownIDs {
		if m := s.getMetadata(id); m != nil && m.IsIntermediateTestVariant && !includeTestVariants {
			continue
		}
		var parseModes []source.ParseMode
		switch mode {
		case source.TypecheckAll:
			if s.workspaceParseMode(id) == source.ParseFull {
				parseModes = []source.ParseMode{source.ParseFull}
			} else {
				parseModes = []source.ParseMode{source.ParseExported, source.ParseFull}
			}
		case source.TypecheckFull:
			parseModes = []source.ParseMode{source.ParseFull}
		case source.TypecheckWorkspace:
			parseModes = []source.ParseMode{s.workspaceParseMode(id)}
		}
		for _, parseMode := range parseModes {
			ph, err := s.buildPackageHandle(ctx, id, parseMode)
			if err != nil {
				return nil, err
			}
			phs = append(phs, ph)
		}
	}
	return phs, nil
}
func (s *snapshot) getOrLoadIDsForURI(ctx context.Context, uri span.URI) ([]PackageID, error) {
	knownIDs := s.getIDsForURI(uri)
	reload := len(knownIDs) == 0
	for _, id := range knownIDs {
		if s.noValidMetadataForID(id) {
			reload = true
			break
		}
	}
	if reload {
		err := s.load(ctx, false, fileURI(uri))
		if !s.useInvalidMetadata() && err != nil {
			return nil, err
		}
		knownIDs = s.getIDsForURI(uri)
		if len(knownIDs) == 0 {
			return nil, err
		}
	}
	return knownIDs, nil
}
func (s *snapshot) useInvalidMetadata() bool {
	return s.view.goversion >= 13 && s.view.Options().ExperimentalUseInvalidMetadata
}
func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]source.Package, error) {
	if err := s.awaitLoaded(ctx); err != nil {
		return nil, err
	}
	ids := make(map[PackageID]struct{})
	s.transitiveReverseDependencies(PackageID(id), ids)
	delete(ids, PackageID(id))
	var pkgs []source.Package
	for id := range ids {
		pkg, err := s.checkedPackage(ctx, id, s.workspaceParseMode(id))
		if err != nil {
			return nil, err
		}
		pkgs = append(pkgs, pkg)
	}
	return pkgs, nil
}
func (s *snapshot) checkedPackage(ctx context.Context, id PackageID, mode source.ParseMode) (*pkg, error) {
	ph, err := s.buildPackageHandle(ctx, id, mode)
	if err != nil {
		return nil, err
	}
	return ph.check(ctx, s)
}