// Copyright 2018 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 packages

import (
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	

	exec 
	
	
	
)

// debug controls verbose logging.
var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG"))

// A goTooOldError reports that the go command
// found by exec.LookPath is too old to use the new go list behavior.
type goTooOldError struct {
	error
}

// responseDeduper wraps a driverResponse, deduplicating its contents.
type responseDeduper struct {
	seenRoots    map[string]bool
	seenPackages map[string]*Package
	dr           *driverResponse
}

func () *responseDeduper {
	return &responseDeduper{
		dr:           &driverResponse{},
		seenRoots:    map[string]bool{},
		seenPackages: map[string]*Package{},
	}
}

// addAll fills in r with a driverResponse.
func ( *responseDeduper) ( *driverResponse) {
	for ,  := range .Packages {
		.addPackage()
	}
	for ,  := range .Roots {
		.addRoot()
	}
	.dr.GoVersion = .GoVersion
}

func ( *responseDeduper) ( *Package) {
	if .seenPackages[.ID] != nil {
		return
	}
	.seenPackages[.ID] = 
	.dr.Packages = append(.dr.Packages, )
}

func ( *responseDeduper) ( string) {
	if .seenRoots[] {
		return
	}
	.seenRoots[] = true
	.dr.Roots = append(.dr.Roots, )
}

type golistState struct {
	cfg *Config
	ctx context.Context

	envOnce    sync.Once
	goEnvError error
	goEnv      map[string]string

	rootsOnce     sync.Once
	rootDirsError error
	rootDirs      map[string]string

	goVersionOnce  sync.Once
	goVersionError error
	goVersion      int // The X in Go 1.X.

	// vendorDirs caches the (non)existence of vendor directories.
	vendorDirs map[string]bool
}

// getEnv returns Go environment variables. Only specific variables are
// populated -- computing all of them is slow.
func ( *golistState) () (map[string]string, error) {
	.envOnce.Do(func() {
		var  *bytes.Buffer
		, .goEnvError = .invokeGo("env", "-json", "GOMOD", "GOPATH")
		if .goEnvError != nil {
			return
		}

		.goEnv = make(map[string]string)
		 := json.NewDecoder()
		if .goEnvError = .Decode(&.goEnv); .goEnvError != nil {
			return
		}
	})
	return .goEnv, .goEnvError
}

// mustGetEnv is a convenience function that can be used if getEnv has already succeeded.
func ( *golistState) () map[string]string {
	,  := .getEnv()
	if  != nil {
		panic(fmt.Sprintf("mustGetEnv: %v", ))
	}
	return 
}

// goListDriver uses the go list command to interpret the patterns and produce
// the build system package structure.
// See driver for more details.
func ( *Config,  ...string) (*driverResponse, error) {
	// Make sure that any asynchronous go commands are killed when we return.
	 := .Context
	if  == nil {
		 = context.Background()
	}
	,  := context.WithCancel()
	defer ()

	 := newDeduper()

	 := &golistState{
		cfg:        ,
		ctx:        ,
		vendorDirs: map[string]bool{},
	}

	// Fill in response.Sizes asynchronously if necessary.
	var  error
	var  sync.WaitGroup
	if .Mode&NeedTypesSizes != 0 || .Mode&NeedTypes != 0 {
		.Add(1)
		go func() {
			var  types.Sizes
			,  = packagesdriver.GetSizesGolist(, .cfgInvocation(), .gocmdRunner)
			// types.SizesFor always returns nil or a *types.StdSizes.
			.dr.Sizes, _ = .(*types.StdSizes)
			.Done()
		}()
	}

	// Determine files requested in contains patterns
	var  []string
	 := make([]string, 0, len())
	// Extract file= and other [querytype]= patterns. Report an error if querytype
	// doesn't exist.
:
	for ,  := range  {
		 := strings.Index(, "=")
		if  < 0 {
			 = append(, )
		} else {
			,  := [:], [+len("="):]
			switch  {
			case "file":
				 = append(, )
			case "pattern":
				 = append(, )
			case "": // not a reserved query
				 = append(, )
			default:
				for ,  := range  {
					if  < 'a' ||  > 'z' { // not a reserved query
						 = append(, )
						continue 
					}
				}
				// Reject all other patterns containing "="
				return nil, fmt.Errorf("invalid query type %q in query pattern %q", , )
			}
		}
	}

	// See if we have any patterns to pass through to go list. Zero initial
	// patterns also requires a go list call, since it's the equivalent of
	// ".".
	if len() > 0 || len() == 0 {
		,  := .createDriverResponse(...)
		if  != nil {
			return nil, 
		}
		.addAll()
	}

	if len() != 0 {
		if  := .runContainsQueries(, );  != nil {
			return nil, 
		}
	}

	// Only use go/packages' overlay processing if we're using a Go version
	// below 1.16. Otherwise, go list handles it.
	if ,  := .getGoVersion();  == nil &&  < 16 {
		, ,  := .processGolistOverlay()
		if  != nil {
			return nil, 
		}

		var  []string
		if len() > 0 {
			 = append(, ...)
			 = append(, ...)
		}
		if  := .addNeededOverlayPackages(, );  != nil {
			return nil, 
		}
		// Check candidate packages for containFiles.
		if len() > 0 {
			for ,  := range  {
				,  := .seenPackages[]
				if ! {
					.addPackage(&Package{
						ID: ,
						Errors: []Error{{
							Kind: ListError,
							Msg:  fmt.Sprintf("package %s expected but not seen", ),
						}},
					})
					continue
				}
				for ,  := range  {
					for ,  := range .GoFiles {
						if sameFile(, ) {
							.addRoot()
						}
					}
				}
			}
		}
		// Add root for any package that matches a pattern. This applies only to
		// packages that are modified by overlays, since they are not added as
		// roots automatically.
		for ,  := range  {
			 := matchPattern()
			for ,  := range  {
				,  := .seenPackages[]
				if ! {
					continue
				}
				if (.PkgPath) {
					.addRoot(.ID)
				}
			}
		}
	}

	.Wait()
	if  != nil {
		return nil, 
	}
	return .dr, nil
}

func ( *golistState) ( *responseDeduper,  []string) error {
	if len() == 0 {
		return nil
	}
	,  := .createDriverResponse(...)
	if  != nil {
		return 
	}
	for ,  := range .Packages {
		.addPackage()
	}
	, ,  := .processGolistOverlay()
	if  != nil {
		return 
	}
	return .(, )
}

func ( *golistState) ( *responseDeduper,  []string) error {
	for ,  := range  {
		// TODO(matloob): Do only one query per directory.
		 := filepath.Dir()
		// Pass absolute path of directory to go list so that it knows to treat it as a directory,
		// not a package path.
		,  := filepath.Abs()
		if  != nil {
			return fmt.Errorf("could not determine absolute path of file= query path %q: %v", , )
		}
		,  := .createDriverResponse()

		// If there was an error loading the package, or no packages are returned,
		// or the package is returned with errors, try to load the file as an
		// ad-hoc package.
		// Usually the error will appear in a returned package, but may not if we're
		// in module mode and the ad-hoc is located outside a module.
		if  != nil || len(.Packages) == 0 || len(.Packages) == 1 && len(.Packages[0].GoFiles) == 0 &&
			len(.Packages[0].Errors) == 1 {
			var  error
			if ,  = .adhocPackage(, );  != nil {
				return  // return the original error
			}
		}
		 := make(map[string]bool, len(.Roots))
		for ,  := range .Roots {
			[] = true
		}
		for ,  := range .Packages {
			// Add any new packages to the main set
			// We don't bother to filter packages that will be dropped by the changes of roots,
			// that will happen anyway during graph construction outside this function.
			// Over-reporting packages is not a problem.
			.addPackage()
			// if the package was not a root one, it cannot have the file
			if ![.ID] {
				continue
			}
			for ,  := range .GoFiles {
				if filepath.Base() == filepath.Base() {
					.addRoot(.ID)
					break
				}
			}
		}
	}
	return nil
}

// adhocPackage attempts to load or construct an ad-hoc package for a given
// query, if the original call to the driver produced inadequate results.
func ( *golistState) (,  string) (*driverResponse, error) {
	,  := .createDriverResponse()
	if  != nil {
		return nil, 
	}
	// If we get nothing back from `go list`,
	// try to make this file into its own ad-hoc package.
	// TODO(rstambler): Should this check against the original response?
	if len(.Packages) == 0 {
		.Packages = append(.Packages, &Package{
			ID:              "command-line-arguments",
			PkgPath:         ,
			GoFiles:         []string{},
			CompiledGoFiles: []string{},
			Imports:         make(map[string]*Package),
		})
		.Roots = append(.Roots, "command-line-arguments")
	}
	// Handle special cases.
	if len(.Packages) == 1 {
		// golang/go#33482: If this is a file= query for ad-hoc packages where
		// the file only exists on an overlay, and exists outside of a module,
		// add the file to the package and remove the errors.
		if .Packages[0].ID == "command-line-arguments" ||
			filepath.ToSlash(.Packages[0].PkgPath) == filepath.ToSlash() {
			if len(.Packages[0].GoFiles) == 0 {
				 := filepath.Join(, filepath.Base()) // avoid recomputing abspath
				// TODO(matloob): check if the file is outside of a root dir?
				for  := range .cfg.Overlay {
					if  ==  {
						.Packages[0].Errors = nil
						.Packages[0].GoFiles = []string{}
						.Packages[0].CompiledGoFiles = []string{}
					}
				}
			}
		}
	}
	return , nil
}

// Fields must match go list;
// see $GOROOT/src/cmd/go/internal/load/pkg.go.
type jsonPackage struct {
	ImportPath        string
	Dir               string
	Name              string
	Export            string
	GoFiles           []string
	CompiledGoFiles   []string
	IgnoredGoFiles    []string
	IgnoredOtherFiles []string
	EmbedPatterns     []string
	EmbedFiles        []string
	CFiles            []string
	CgoFiles          []string
	CXXFiles          []string
	MFiles            []string
	HFiles            []string
	FFiles            []string
	SFiles            []string
	SwigFiles         []string
	SwigCXXFiles      []string
	SysoFiles         []string
	Imports           []string
	ImportMap         map[string]string
	Deps              []string
	Module            *Module
	TestGoFiles       []string
	TestImports       []string
	XTestGoFiles      []string
	XTestImports      []string
	ForTest           string // q in a "p [q.test]" package, else ""
	DepOnly           bool

	Error      *packagesinternal.PackageError
	DepsErrors []*packagesinternal.PackageError
}

type jsonPackageError struct {
	ImportStack []string
	Pos         string
	Err         string
}

func ( *jsonPackage) [][]string {
	return [][]string{.CFiles, .CXXFiles, .MFiles, .HFiles, .FFiles, .SFiles, .SwigFiles, .SwigCXXFiles, .SysoFiles}
}

// createDriverResponse uses the "go list" command to expand the pattern
// words and return a response for the specified packages.
func ( *golistState) ( ...string) (*driverResponse, error) {
	// go list uses the following identifiers in ImportPath and Imports:
	//
	// 	"p"			-- importable package or main (command)
	// 	"q.test"		-- q's test executable
	// 	"p [q.test]"		-- variant of p as built for q's test executable
	// 	"q_test [q.test]"	-- q's external test package
	//
	// The packages p that are built differently for a test q.test
	// are q itself, plus any helpers used by the external test q_test,
	// typically including "testing" and all its dependencies.

	// Run "go list" for complete
	// information on the specified packages.
	,  := .getGoVersion()
	if  != nil {
		return nil, 
	}
	,  := .invokeGo("list", golistargs(.cfg, , )...)
	if  != nil {
		return nil, 
	}

	 := make(map[string]*jsonPackage)
	 := make(map[string]*Package)
	 := make(map[string][]Error)
	// Decode the JSON and convert it to Package form.
	 := &driverResponse{
		GoVersion: ,
	}
	for  := json.NewDecoder(); .More(); {
		 := new(jsonPackage)
		if  := .Decode();  != nil {
			return nil, fmt.Errorf("JSON decoding failed: %v", )
		}

		if .ImportPath == "" {
			// The documentation for go list says that “[e]rroneous packages will have
			// a non-empty ImportPath”. If for some reason it comes back empty, we
			// prefer to error out rather than silently discarding data or handing
			// back a package without any way to refer to it.
			if .Error != nil {
				return nil, Error{
					Pos: .Error.Pos,
					Msg: .Error.Err,
				}
			}
			return nil, fmt.Errorf("package missing import path: %+v", )
		}

		// Work around https://golang.org/issue/33157:
		// go list -e, when given an absolute path, will find the package contained at
		// that directory. But when no package exists there, it will return a fake package
		// with an error and the ImportPath set to the absolute path provided to go list.
		// Try to convert that absolute path to what its package path would be if it's
		// contained in a known module or GOPATH entry. This will allow the package to be
		// properly "reclaimed" when overlays are processed.
		if filepath.IsAbs(.ImportPath) && .Error != nil {
			, ,  := .getPkgPath(.ImportPath)
			if  != nil {
				return nil, 
			}
			if  {
				.ImportPath = 
			}
		}

		if ,  := [.ImportPath];  {
			// If one version of the package has an error, and the other doesn't, assume
			// that this is a case where go list is reporting a fake dependency variant
			// of the imported package: When a package tries to invalidly import another
			// package, go list emits a variant of the imported package (with the same
			// import path, but with an error on it, and the package will have a
			// DepError set on it). An example of when this can happen is for imports of
			// main packages: main packages can not be imported, but they may be
			// separately matched and listed by another pattern.
			// See golang.org/issue/36188 for more details.

			// The plan is that eventually, hopefully in Go 1.15, the error will be
			// reported on the importing package rather than the duplicate "fake"
			// version of the imported package. Once all supported versions of Go
			// have the new behavior this logic can be deleted.
			// TODO(matloob): delete the workaround logic once all supported versions of
			// Go return the errors on the proper package.

			// There should be exactly one version of a package that doesn't have an
			// error.
			if .Error == nil && .Error == nil {
				if !reflect.DeepEqual(, ) {
					return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", .ImportPath)
				}
				continue
			}

			// Determine if this package's error needs to be bubbled up.
			// This is a hack, and we expect for go list to eventually set the error
			// on the package.
			if .Error != nil {
				var  string
				if strings.Contains(.Error.Err, "not an importable package") {
					 = "not an importable package"
				} else if strings.Contains(.Error.Err, "use of internal package") && strings.Contains(.Error.Err, "not allowed") {
					 = "use of internal package not allowed"
				}
				if  != "" {
					if len(.Error.ImportStack) < 1 {
						return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, )
					}
					 := .Error.ImportStack[len(.Error.ImportStack)-1]
					if  == .ImportPath {
						// Using an older version of Go which put this package itself on top of import
						// stack, instead of the importer. Look for importer in second from top
						// position.
						if len(.Error.ImportStack) < 2 {
							return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, )
						}
						 = .Error.ImportStack[len(.Error.ImportStack)-2]
					}
					[] = append([], Error{
						Pos:  .Error.Pos,
						Msg:  .Error.Err,
						Kind: ListError,
					})
				}
			}

			// Make sure that if there's a version of the package without an error,
			// that's the one reported to the user.
			if .Error == nil {
				continue
			}

			// This package will replace the old one at the end of the loop.
		}
		[.ImportPath] = 

		 := &Package{
			Name:            .Name,
			ID:              .ImportPath,
			GoFiles:         absJoin(.Dir, .GoFiles, .CgoFiles),
			CompiledGoFiles: absJoin(.Dir, .CompiledGoFiles),
			OtherFiles:      absJoin(.Dir, otherFiles()...),
			EmbedFiles:      absJoin(.Dir, .EmbedFiles),
			EmbedPatterns:   absJoin(.Dir, .EmbedPatterns),
			IgnoredFiles:    absJoin(.Dir, .IgnoredGoFiles, .IgnoredOtherFiles),
			forTest:         .ForTest,
			depsErrors:      .DepsErrors,
			Module:          .Module,
		}

		if (.cfg.Mode&typecheckCgo) != 0 && len(.CgoFiles) != 0 {
			if len(.CompiledGoFiles) > len(.GoFiles) {
				// We need the cgo definitions, which are in the first
				// CompiledGoFile after the non-cgo ones. This is a hack but there
				// isn't currently a better way to find it. We also need the pure
				// Go files and unprocessed cgo files, all of which are already
				// in pkg.GoFiles.
				 := .CompiledGoFiles[len(.GoFiles)]
				.CompiledGoFiles = append([]string{}, .GoFiles...)
			} else {
				// golang/go#38990: go list silently fails to do cgo processing
				.CompiledGoFiles = nil
				.Errors = append(.Errors, Error{
					Msg:  "go list failed to return CompiledGoFiles. This may indicate failure to perform cgo processing; try building at the command line. See https://golang.org/issue/38990.",
					Kind: ListError,
				})
			}
		}

		// Work around https://golang.org/issue/28749:
		// cmd/go puts assembly, C, and C++ files in CompiledGoFiles.
		// Remove files from CompiledGoFiles that are non-go files
		// (or are not files that look like they are from the cache).
		if len(.CompiledGoFiles) > 0 {
			 := .CompiledGoFiles[:0]
			for ,  := range .CompiledGoFiles {
				if  := filepath.Ext();  != ".go" &&  != "" { // ext == "" means the file is from the cache, so probably cgo-processed file
					continue
				}
				 = append(, )
			}
			.CompiledGoFiles = 
		}

		// Extract the PkgPath from the package's ID.
		if  := strings.IndexByte(.ID, ' ');  >= 0 {
			.PkgPath = .ID[:]
		} else {
			.PkgPath = .ID
		}

		if .PkgPath == "unsafe" {
			.GoFiles = nil // ignore fake unsafe.go file
		}

		// Assume go list emits only absolute paths for Dir.
		if .Dir != "" && !filepath.IsAbs(.Dir) {
			log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", .Dir)
		}

		if .Export != "" && !filepath.IsAbs(.Export) {
			.ExportFile = filepath.Join(.Dir, .Export)
		} else {
			.ExportFile = .Export
		}

		// imports
		//
		// Imports contains the IDs of all imported packages.
		// ImportsMap records (path, ID) only where they differ.
		 := make(map[string]bool)
		for ,  := range .Imports {
			[] = true
		}
		.Imports = make(map[string]*Package)
		for ,  := range .ImportMap {
			.Imports[] = &Package{ID: } // non-identity import
			delete(, )
		}
		for  := range  {
			if  == "C" {
				continue
			}

			.Imports[] = &Package{ID: } // identity import
		}
		if !.DepOnly {
			.Roots = append(.Roots, .ID)
		}

		// Work around for pre-go.1.11 versions of go list.
		// TODO(matloob): they should be handled by the fallback.
		// Can we delete this?
		if len(.CompiledGoFiles) == 0 {
			.CompiledGoFiles = .GoFiles
		}

		// Temporary work-around for golang/go#39986. Parse filenames out of
		// error messages. This happens if there are unrecoverable syntax
		// errors in the source, so we can't match on a specific error message.
		if  := .Error;  != nil && .shouldAddFilenameFromError() {
			 := func( string) bool {
				 := strings.Split(, ":")
				if len() < 1 {
					return false
				}
				 := strings.TrimSpace([0])
				if  == "" {
					return false
				}
				if !filepath.IsAbs() {
					 = filepath.Join(.cfg.Dir, )
				}
				,  := os.Stat()
				if  == nil {
					return false
				}
				.CompiledGoFiles = append(.CompiledGoFiles, )
				.GoFiles = append(.GoFiles, )
				return true
			}
			 := (.Pos)
			// In some cases, go list only reports the error position in the
			// error text, not the error position. One such case is when the
			// file's package name is a keyword (see golang.org/issue/39763).
			if ! {
				(.Err)
			}
		}

		if .Error != nil {
			 := strings.TrimSpace(.Error.Err) // Trim to work around golang.org/issue/32363.
			// Address golang.org/issue/35964 by appending import stack to error message.
			if  == "import cycle not allowed" && len(.Error.ImportStack) != 0 {
				 += fmt.Sprintf(": import stack: %v", .Error.ImportStack)
			}
			.Errors = append(.Errors, Error{
				Pos:  .Error.Pos,
				Msg:  ,
				Kind: ListError,
			})
		}

		[.ID] = 
	}

	for ,  := range  {
		if ,  := [];  {
			.Errors = append(.Errors, ...)
		}
	}
	for ,  := range  {
		.Packages = append(.Packages, )
	}
	sort.Slice(.Packages, func(,  int) bool { return .Packages[].ID < .Packages[].ID })

	return , nil
}

func ( *golistState) ( *jsonPackage) bool {
	if len(.GoFiles) > 0 || len(.CompiledGoFiles) > 0 {
		return false
	}

	,  := .getGoVersion()
	if  != nil {
		return false
	}

	// On Go 1.14 and earlier, only add filenames from errors if the import stack is empty.
	// The import stack behaves differently for these versions than newer Go versions.
	if  < 15 {
		return len(.Error.ImportStack) == 0
	}

	// On Go 1.15 and later, only parse filenames out of error if there's no import stack,
	// or the current package is at the top of the import stack. This is not guaranteed
	// to work perfectly, but should avoid some cases where files in errors don't belong to this
	// package.
	return len(.Error.ImportStack) == 0 || .Error.ImportStack[len(.Error.ImportStack)-1] == .ImportPath
}

// getGoVersion returns the effective minor version of the go command.
func ( *golistState) () (int, error) {
	.goVersionOnce.Do(func() {
		.goVersion, .goVersionError = gocommand.GoVersion(.ctx, .cfgInvocation(), .cfg.gocmdRunner)
	})
	return .goVersion, .goVersionError
}

// getPkgPath finds the package path of a directory if it's relative to a root
// directory.
func ( *golistState) ( string) (string, bool, error) {
	,  := filepath.Abs()
	if  != nil {
		return "", false, 
	}
	,  := .determineRootDirs()
	if  != nil {
		return "", false, 
	}

	for ,  := range  {
		// Make sure that the directory is in the module,
		// to avoid creating a path relative to another module.
		if !strings.HasPrefix(, ) {
			continue
		}
		// TODO(matloob): This doesn't properly handle symlinks.
		,  := filepath.Rel(, )
		if  != nil {
			continue
		}
		if  != "" {
			// We choose only one root even though the directory even it can belong in multiple modules
			// or GOPATH entries. This is okay because we only need to work with absolute dirs when a
			// file is missing from disk, for instance when gopls calls go/packages in an overlay.
			// Once the file is saved, gopls, or the next invocation of the tool will get the correct
			// result straight from golist.
			// TODO(matloob): Implement module tiebreaking?
			return path.Join(, filepath.ToSlash()), true, nil
		}
		return filepath.ToSlash(), true, nil
	}
	return "", false, nil
}

// absJoin absolutizes and flattens the lists of files.
func ( string,  ...[]string) ( []string) {
	for ,  := range  {
		for ,  := range  {
			if !filepath.IsAbs() {
				 = filepath.Join(, )
			}
			 = append(, )
		}
	}
	return 
}

func ( *Config,  int) string {
	if  < 19 {
		return "-json"
	}
	var  []string
	 := make(map[string]bool)
	 := func( ...string) {
		for ,  := range  {
			if ![] {
				[] = true
				 = append(, )
			}
		}
	}
	("Name", "ImportPath", "Error") // These fields are always needed
	if .Mode&NeedFiles != 0 || .Mode&NeedTypes != 0 {
		("Dir", "GoFiles", "IgnoredGoFiles", "IgnoredOtherFiles", "CFiles",
			"CgoFiles", "CXXFiles", "MFiles", "HFiles", "FFiles", "SFiles",
			"SwigFiles", "SwigCXXFiles", "SysoFiles")
		if .Tests {
			("TestGoFiles", "XTestGoFiles")
		}
	}
	if .Mode&NeedTypes != 0 {
		// CompiledGoFiles seems to be required for the test case TestCgoNoSyntax,
		// even when -compiled isn't passed in.
		// TODO(#52435): Should we make the test ask for -compiled, or automatically
		// request CompiledGoFiles in certain circumstances?
		("Dir", "CompiledGoFiles")
	}
	if .Mode&NeedCompiledGoFiles != 0 {
		("Dir", "CompiledGoFiles", "Export")
	}
	if .Mode&NeedImports != 0 {
		// When imports are requested, DepOnly is used to distinguish between packages
		// explicitly requested and transitive imports of those packages.
		("DepOnly", "Imports", "ImportMap")
		if .Tests {
			("TestImports", "XTestImports")
		}
	}
	if .Mode&NeedDeps != 0 {
		("DepOnly")
	}
	if usesExportData() {
		// Request Dir in the unlikely case Export is not absolute.
		("Dir", "Export")
	}
	if .Mode&needInternalForTest != 0 {
		("ForTest")
	}
	if .Mode&needInternalDepsErrors != 0 {
		("DepsErrors")
	}
	if .Mode&NeedModule != 0 {
		("Module")
	}
	if .Mode&NeedEmbedFiles != 0 {
		("EmbedFiles")
	}
	if .Mode&NeedEmbedPatterns != 0 {
		("EmbedPatterns")
	}
	return "-json=" + strings.Join(, ",")
}

func ( *Config,  []string,  int) []string {
	const  = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
	 := []string{
		"-e", jsonFlag(, ),
		fmt.Sprintf("-compiled=%t", .Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),
		fmt.Sprintf("-test=%t", .Tests),
		fmt.Sprintf("-export=%t", usesExportData()),
		fmt.Sprintf("-deps=%t", .Mode&NeedImports != 0),
		// go list doesn't let you pass -test and -find together,
		// probably because you'd just get the TestMain.
		fmt.Sprintf("-find=%t", !.Tests && .Mode& == 0 && !usesExportData()),
	}
	 = append(, .BuildFlags...)
	 = append(, "--")
	 = append(, ...)
	return 
}

// cfgInvocation returns an Invocation that reflects cfg's settings.
func ( *golistState) () gocommand.Invocation {
	 := .cfg
	return gocommand.Invocation{
		BuildFlags: .BuildFlags,
		ModFile:    .modFile,
		ModFlag:    .modFlag,
		CleanEnv:   .Env != nil,
		Env:        .Env,
		Logf:       .Logf,
		WorkingDir: .Dir,
	}
}

// invokeGo returns the stdout of a go command invocation.
func ( *golistState) ( string,  ...string) (*bytes.Buffer, error) {
	 := .cfg

	 := .cfgInvocation()

	// For Go versions 1.16 and above, `go list` accepts overlays directly via
	// the -overlay flag. Set it, if it's available.
	//
	// The check for "list" is not necessarily required, but we should avoid
	// getting the go version if possible.
	if  == "list" {
		,  := .getGoVersion()
		if  != nil {
			return nil, 
		}
		if  >= 16 {
			, ,  := .writeOverlays()
			if  != nil {
				return nil, 
			}
			defer ()
			.Overlay = 
		}
	}
	.Verb = 
	.Args = 
	 := .gocmdRunner
	if  == nil {
		 = &gocommand.Runner{}
	}
	, , ,  := .RunRaw(.Context, )
	if  != nil {
		// Check for 'go' executable not being found.
		if ,  := .(*exec.Error);  && .Err == exec.ErrNotFound {
			return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
		}

		,  := .(*exec.ExitError)
		if ! {
			// Catastrophic error:
			// - context cancellation
			return nil, fmt.Errorf("couldn't run 'go': %w", )
		}

		// Old go version?
		if strings.Contains(.String(), "flag provided but not defined") {
			return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", , )}
		}

		// Related to #24854
		if len(.String()) > 0 && strings.Contains(.String(), "unexpected directory layout") {
			return nil, 
		}

		// Is there an error running the C compiler in cgo? This will be reported in the "Error" field
		// and should be suppressed by go list -e.
		//
		// This condition is not perfect yet because the error message can include other error messages than runtime/cgo.
		 := func( rune) bool {
			// From https://golang.org/ref/spec#Import_declarations:
			//    Implementation restriction: A compiler may restrict ImportPaths to non-empty strings
			//    using only characters belonging to Unicode's L, M, N, P, and S general categories
			//    (the Graphic characters without spaces) and may also exclude the
			//    characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD.
			return unicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, ) &&
				!strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", )
		}
		// golang/go#36770: Handle case where cmd/go prints module download messages before the error.
		 := .String()
		for strings.HasPrefix(, "go: downloading") {
			 = [strings.IndexRune(, '\n')+1:]
		}
		if len(.String()) > 0 && strings.HasPrefix(.String(), "# ") {
			 := [len("# "):]
			if strings.HasPrefix(strings.TrimLeftFunc(, ), "\n") {
				return , nil
			}
			// Treat pkg-config errors as a special case (golang.org/issue/36770).
			if strings.HasPrefix(, "pkg-config") {
				return , nil
			}
		}

		// This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show
		// the error in the Err section of stdout in case -e option is provided.
		// This fix is provided for backwards compatibility.
		if len(.String()) > 0 && strings.Contains(.String(), "named files must be .go files") {
			 := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
				strings.Trim(.String(), "\n"))
			return bytes.NewBufferString(), nil
		}

		// Similar to the previous error, but currently lacks a fix in Go.
		if len(.String()) > 0 && strings.Contains(.String(), "named files must all be in one directory") {
			 := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
				strings.Trim(.String(), "\n"))
			return bytes.NewBufferString(), nil
		}

		// Backwards compatibility for Go 1.11 because 1.12 and 1.13 put the directory in the ImportPath.
		// If the package doesn't exist, put the absolute path of the directory into the error message,
		// as Go 1.13 list does.
		const  = "no such directory"
		if len(.String()) > 0 && strings.Contains(.String(), ) {
			 := .String()
			 := strings.TrimSpace([strings.Index(, )+len():])
			 := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
				, strings.Trim(.String(), "\n"))
			return bytes.NewBufferString(), nil
		}

		// Workaround for #29280: go list -e has incorrect behavior when an ad-hoc package doesn't exist.
		// Note that the error message we look for in this case is different that the one looked for above.
		if len(.String()) > 0 && strings.Contains(.String(), "no such file or directory") {
			 := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
				strings.Trim(.String(), "\n"))
			return bytes.NewBufferString(), nil
		}

		// Workaround for #34273. go list -e with GO111MODULE=on has incorrect behavior when listing a
		// directory outside any module.
		if len(.String()) > 0 && strings.Contains(.String(), "outside available modules") {
			 := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
				// TODO(matloob): command-line-arguments isn't correct here.
				"command-line-arguments", strings.Trim(.String(), "\n"))
			return bytes.NewBufferString(), nil
		}

		// Another variation of the previous error
		if len(.String()) > 0 && strings.Contains(.String(), "outside module root") {
			 := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
				// TODO(matloob): command-line-arguments isn't correct here.
				"command-line-arguments", strings.Trim(.String(), "\n"))
			return bytes.NewBufferString(), nil
		}

		// Workaround for an instance of golang.org/issue/26755: go list -e  will return a non-zero exit
		// status if there's a dependency on a package that doesn't exist. But it should return
		// a zero exit status and set an error on that package.
		if len(.String()) > 0 && strings.Contains(.String(), "no Go files in") {
			// Don't clobber stdout if `go list` actually returned something.
			if len(.String()) > 0 {
				return , nil
			}
			// try to extract package name from string
			 := .String()
			var  string
			 := strings.Index(, ":")
			if  > 0 && strings.HasPrefix(, "go build ") {
				 = [len("go build "):]
			}
			 := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
				, strings.Trim(, "\n"))
			return bytes.NewBufferString(), nil
		}

		// Export mode entails a build.
		// If that build fails, errors appear on stderr
		// (despite the -e flag) and the Export field is blank.
		// Do not fail in that case.
		// The same is true if an ad-hoc package given to go list doesn't exist.
		// TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when
		// packages don't exist or a build fails.
		if !usesExportData() && !containsGoFile() {
			return nil, 
		}
	}
	return , nil
}

// OverlayJSON is the format overlay files are expected to be in.
// The Replace map maps from overlaid paths to replacement paths:
// the Go command will forward all reads trying to open
// each overlaid path to its replacement path, or consider the overlaid
// path not to exist if the replacement path is empty.
//
// From golang/go#39958.
type OverlayJSON struct {
	Replace map[string]string `json:"replace,omitempty"`
}

// writeOverlays writes out files for go list's -overlay flag, as described
// above.
func ( *golistState) () ( string,  func(),  error) {
	// Do nothing if there are no overlays in the config.
	if len(.cfg.Overlay) == 0 {
		return "", func() {}, nil
	}
	,  := ioutil.TempDir("", "gopackages-*")
	if  != nil {
		return "", nil, 
	}
	// The caller must clean up this directory, unless this function returns an
	// error.
	 = func() {
		os.RemoveAll()
	}
	defer func() {
		if  != nil {
			()
		}
	}()
	 := map[string]string{}
	for ,  := range .cfg.Overlay {
		// Create a unique filename for the overlaid files, to avoid
		// creating nested directories.
		 := strings.Join(strings.Split(filepath.ToSlash(), "/"), "")
		,  := ioutil.TempFile(, fmt.Sprintf("*-%s", ))
		if  != nil {
			return "", func() {}, 
		}
		if ,  := .Write();  != nil {
			return "", func() {}, 
		}
		if  := .Close();  != nil {
			return "", func() {}, 
		}
		[] = .Name()
	}
	,  := json.Marshal(OverlayJSON{Replace: })
	if  != nil {
		return "", func() {}, 
	}
	// Write out the overlay file that contains the filepath mappings.
	 = filepath.Join(, "overlay.json")
	if  := ioutil.WriteFile(, , 0665);  != nil {
		return "", func() {}, 
	}
	return , , nil
}

func ( []string) bool {
	for ,  := range  {
		if strings.HasSuffix(, ".go") {
			return true
		}
	}
	return false
}

func ( *exec.Cmd) string {
	 := make(map[string]string)
	for ,  := range .Env {
		 := strings.SplitN(, "=", 2)
		,  := [0], [1]
		[] = 
	}

	var  []string
	for ,  := range .Args {
		 := strconv.Quote()
		if [1:len()-1] !=  || strings.Contains(, " ") {
			 = append(, )
		} else {
			 = append(, )
		}
	}
	return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", ["GOROOT"], ["GOPATH"], ["GO111MODULE"], ["GOPROXY"], ["PWD"], strings.Join(, " "))
}