github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/go/packages/golist.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package packages
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"go/types"
    13  	"io/ioutil"
    14  	"log"
    15  	"os"
    16  	"path"
    17  	"path/filepath"
    18  	"reflect"
    19  	"sort"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  	"unicode"
    24  
    25  	exec "golang.org/x/sys/execabs"
    26  	"github.com/jhump/golang-x-tools/go/internal/packagesdriver"
    27  	"github.com/jhump/golang-x-tools/internal/gocommand"
    28  	"github.com/jhump/golang-x-tools/internal/packagesinternal"
    29  	"golang.org/x/xerrors"
    30  )
    31  
    32  // debug controls verbose logging.
    33  var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG"))
    34  
    35  // A goTooOldError reports that the go command
    36  // found by exec.LookPath is too old to use the new go list behavior.
    37  type goTooOldError struct {
    38  	error
    39  }
    40  
    41  // responseDeduper wraps a driverResponse, deduplicating its contents.
    42  type responseDeduper struct {
    43  	seenRoots    map[string]bool
    44  	seenPackages map[string]*Package
    45  	dr           *driverResponse
    46  }
    47  
    48  func newDeduper() *responseDeduper {
    49  	return &responseDeduper{
    50  		dr:           &driverResponse{},
    51  		seenRoots:    map[string]bool{},
    52  		seenPackages: map[string]*Package{},
    53  	}
    54  }
    55  
    56  // addAll fills in r with a driverResponse.
    57  func (r *responseDeduper) addAll(dr *driverResponse) {
    58  	for _, pkg := range dr.Packages {
    59  		r.addPackage(pkg)
    60  	}
    61  	for _, root := range dr.Roots {
    62  		r.addRoot(root)
    63  	}
    64  }
    65  
    66  func (r *responseDeduper) addPackage(p *Package) {
    67  	if r.seenPackages[p.ID] != nil {
    68  		return
    69  	}
    70  	r.seenPackages[p.ID] = p
    71  	r.dr.Packages = append(r.dr.Packages, p)
    72  }
    73  
    74  func (r *responseDeduper) addRoot(id string) {
    75  	if r.seenRoots[id] {
    76  		return
    77  	}
    78  	r.seenRoots[id] = true
    79  	r.dr.Roots = append(r.dr.Roots, id)
    80  }
    81  
    82  type golistState struct {
    83  	cfg *Config
    84  	ctx context.Context
    85  
    86  	envOnce    sync.Once
    87  	goEnvError error
    88  	goEnv      map[string]string
    89  
    90  	rootsOnce     sync.Once
    91  	rootDirsError error
    92  	rootDirs      map[string]string
    93  
    94  	goVersionOnce  sync.Once
    95  	goVersionError error
    96  	goVersion      int // The X in Go 1.X.
    97  
    98  	// vendorDirs caches the (non)existence of vendor directories.
    99  	vendorDirs map[string]bool
   100  }
   101  
   102  // getEnv returns Go environment variables. Only specific variables are
   103  // populated -- computing all of them is slow.
   104  func (state *golistState) getEnv() (map[string]string, error) {
   105  	state.envOnce.Do(func() {
   106  		var b *bytes.Buffer
   107  		b, state.goEnvError = state.invokeGo("env", "-json", "GOMOD", "GOPATH")
   108  		if state.goEnvError != nil {
   109  			return
   110  		}
   111  
   112  		state.goEnv = make(map[string]string)
   113  		decoder := json.NewDecoder(b)
   114  		if state.goEnvError = decoder.Decode(&state.goEnv); state.goEnvError != nil {
   115  			return
   116  		}
   117  	})
   118  	return state.goEnv, state.goEnvError
   119  }
   120  
   121  // mustGetEnv is a convenience function that can be used if getEnv has already succeeded.
   122  func (state *golistState) mustGetEnv() map[string]string {
   123  	env, err := state.getEnv()
   124  	if err != nil {
   125  		panic(fmt.Sprintf("mustGetEnv: %v", err))
   126  	}
   127  	return env
   128  }
   129  
   130  // goListDriver uses the go list command to interpret the patterns and produce
   131  // the build system package structure.
   132  // See driver for more details.
   133  func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
   134  	// Make sure that any asynchronous go commands are killed when we return.
   135  	parentCtx := cfg.Context
   136  	if parentCtx == nil {
   137  		parentCtx = context.Background()
   138  	}
   139  	ctx, cancel := context.WithCancel(parentCtx)
   140  	defer cancel()
   141  
   142  	response := newDeduper()
   143  
   144  	state := &golistState{
   145  		cfg:        cfg,
   146  		ctx:        ctx,
   147  		vendorDirs: map[string]bool{},
   148  	}
   149  
   150  	// Fill in response.Sizes asynchronously if necessary.
   151  	var sizeserr error
   152  	var sizeswg sync.WaitGroup
   153  	if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
   154  		sizeswg.Add(1)
   155  		go func() {
   156  			var sizes types.Sizes
   157  			sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner)
   158  			// types.SizesFor always returns nil or a *types.StdSizes.
   159  			response.dr.Sizes, _ = sizes.(*types.StdSizes)
   160  			sizeswg.Done()
   161  		}()
   162  	}
   163  
   164  	// Determine files requested in contains patterns
   165  	var containFiles []string
   166  	restPatterns := make([]string, 0, len(patterns))
   167  	// Extract file= and other [querytype]= patterns. Report an error if querytype
   168  	// doesn't exist.
   169  extractQueries:
   170  	for _, pattern := range patterns {
   171  		eqidx := strings.Index(pattern, "=")
   172  		if eqidx < 0 {
   173  			restPatterns = append(restPatterns, pattern)
   174  		} else {
   175  			query, value := pattern[:eqidx], pattern[eqidx+len("="):]
   176  			switch query {
   177  			case "file":
   178  				containFiles = append(containFiles, value)
   179  			case "pattern":
   180  				restPatterns = append(restPatterns, value)
   181  			case "": // not a reserved query
   182  				restPatterns = append(restPatterns, pattern)
   183  			default:
   184  				for _, rune := range query {
   185  					if rune < 'a' || rune > 'z' { // not a reserved query
   186  						restPatterns = append(restPatterns, pattern)
   187  						continue extractQueries
   188  					}
   189  				}
   190  				// Reject all other patterns containing "="
   191  				return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
   192  			}
   193  		}
   194  	}
   195  
   196  	// See if we have any patterns to pass through to go list. Zero initial
   197  	// patterns also requires a go list call, since it's the equivalent of
   198  	// ".".
   199  	if len(restPatterns) > 0 || len(patterns) == 0 {
   200  		dr, err := state.createDriverResponse(restPatterns...)
   201  		if err != nil {
   202  			return nil, err
   203  		}
   204  		response.addAll(dr)
   205  	}
   206  
   207  	if len(containFiles) != 0 {
   208  		if err := state.runContainsQueries(response, containFiles); err != nil {
   209  			return nil, err
   210  		}
   211  	}
   212  
   213  	// Only use go/packages' overlay processing if we're using a Go version
   214  	// below 1.16. Otherwise, go list handles it.
   215  	if goVersion, err := state.getGoVersion(); err == nil && goVersion < 16 {
   216  		modifiedPkgs, needPkgs, err := state.processGolistOverlay(response)
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  
   221  		var containsCandidates []string
   222  		if len(containFiles) > 0 {
   223  			containsCandidates = append(containsCandidates, modifiedPkgs...)
   224  			containsCandidates = append(containsCandidates, needPkgs...)
   225  		}
   226  		if err := state.addNeededOverlayPackages(response, needPkgs); err != nil {
   227  			return nil, err
   228  		}
   229  		// Check candidate packages for containFiles.
   230  		if len(containFiles) > 0 {
   231  			for _, id := range containsCandidates {
   232  				pkg, ok := response.seenPackages[id]
   233  				if !ok {
   234  					response.addPackage(&Package{
   235  						ID: id,
   236  						Errors: []Error{{
   237  							Kind: ListError,
   238  							Msg:  fmt.Sprintf("package %s expected but not seen", id),
   239  						}},
   240  					})
   241  					continue
   242  				}
   243  				for _, f := range containFiles {
   244  					for _, g := range pkg.GoFiles {
   245  						if sameFile(f, g) {
   246  							response.addRoot(id)
   247  						}
   248  					}
   249  				}
   250  			}
   251  		}
   252  		// Add root for any package that matches a pattern. This applies only to
   253  		// packages that are modified by overlays, since they are not added as
   254  		// roots automatically.
   255  		for _, pattern := range restPatterns {
   256  			match := matchPattern(pattern)
   257  			for _, pkgID := range modifiedPkgs {
   258  				pkg, ok := response.seenPackages[pkgID]
   259  				if !ok {
   260  					continue
   261  				}
   262  				if match(pkg.PkgPath) {
   263  					response.addRoot(pkg.ID)
   264  				}
   265  			}
   266  		}
   267  	}
   268  
   269  	sizeswg.Wait()
   270  	if sizeserr != nil {
   271  		return nil, sizeserr
   272  	}
   273  	return response.dr, nil
   274  }
   275  
   276  func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error {
   277  	if len(pkgs) == 0 {
   278  		return nil
   279  	}
   280  	dr, err := state.createDriverResponse(pkgs...)
   281  	if err != nil {
   282  		return err
   283  	}
   284  	for _, pkg := range dr.Packages {
   285  		response.addPackage(pkg)
   286  	}
   287  	_, needPkgs, err := state.processGolistOverlay(response)
   288  	if err != nil {
   289  		return err
   290  	}
   291  	return state.addNeededOverlayPackages(response, needPkgs)
   292  }
   293  
   294  func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error {
   295  	for _, query := range queries {
   296  		// TODO(matloob): Do only one query per directory.
   297  		fdir := filepath.Dir(query)
   298  		// Pass absolute path of directory to go list so that it knows to treat it as a directory,
   299  		// not a package path.
   300  		pattern, err := filepath.Abs(fdir)
   301  		if err != nil {
   302  			return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err)
   303  		}
   304  		dirResponse, err := state.createDriverResponse(pattern)
   305  
   306  		// If there was an error loading the package, or the package is returned
   307  		// with errors, try to load the file as an ad-hoc package.
   308  		// Usually the error will appear in a returned package, but may not if we're
   309  		// in module mode and the ad-hoc is located outside a module.
   310  		if err != nil || len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].GoFiles) == 0 &&
   311  			len(dirResponse.Packages[0].Errors) == 1 {
   312  			var queryErr error
   313  			if dirResponse, queryErr = state.adhocPackage(pattern, query); queryErr != nil {
   314  				return err // return the original error
   315  			}
   316  		}
   317  		isRoot := make(map[string]bool, len(dirResponse.Roots))
   318  		for _, root := range dirResponse.Roots {
   319  			isRoot[root] = true
   320  		}
   321  		for _, pkg := range dirResponse.Packages {
   322  			// Add any new packages to the main set
   323  			// We don't bother to filter packages that will be dropped by the changes of roots,
   324  			// that will happen anyway during graph construction outside this function.
   325  			// Over-reporting packages is not a problem.
   326  			response.addPackage(pkg)
   327  			// if the package was not a root one, it cannot have the file
   328  			if !isRoot[pkg.ID] {
   329  				continue
   330  			}
   331  			for _, pkgFile := range pkg.GoFiles {
   332  				if filepath.Base(query) == filepath.Base(pkgFile) {
   333  					response.addRoot(pkg.ID)
   334  					break
   335  				}
   336  			}
   337  		}
   338  	}
   339  	return nil
   340  }
   341  
   342  // adhocPackage attempts to load or construct an ad-hoc package for a given
   343  // query, if the original call to the driver produced inadequate results.
   344  func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) {
   345  	response, err := state.createDriverResponse(query)
   346  	if err != nil {
   347  		return nil, err
   348  	}
   349  	// If we get nothing back from `go list`,
   350  	// try to make this file into its own ad-hoc package.
   351  	// TODO(rstambler): Should this check against the original response?
   352  	if len(response.Packages) == 0 {
   353  		response.Packages = append(response.Packages, &Package{
   354  			ID:              "command-line-arguments",
   355  			PkgPath:         query,
   356  			GoFiles:         []string{query},
   357  			CompiledGoFiles: []string{query},
   358  			Imports:         make(map[string]*Package),
   359  		})
   360  		response.Roots = append(response.Roots, "command-line-arguments")
   361  	}
   362  	// Handle special cases.
   363  	if len(response.Packages) == 1 {
   364  		// golang/go#33482: If this is a file= query for ad-hoc packages where
   365  		// the file only exists on an overlay, and exists outside of a module,
   366  		// add the file to the package and remove the errors.
   367  		if response.Packages[0].ID == "command-line-arguments" ||
   368  			filepath.ToSlash(response.Packages[0].PkgPath) == filepath.ToSlash(query) {
   369  			if len(response.Packages[0].GoFiles) == 0 {
   370  				filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath
   371  				// TODO(matloob): check if the file is outside of a root dir?
   372  				for path := range state.cfg.Overlay {
   373  					if path == filename {
   374  						response.Packages[0].Errors = nil
   375  						response.Packages[0].GoFiles = []string{path}
   376  						response.Packages[0].CompiledGoFiles = []string{path}
   377  					}
   378  				}
   379  			}
   380  		}
   381  	}
   382  	return response, nil
   383  }
   384  
   385  // Fields must match go list;
   386  // see $GOROOT/src/cmd/go/internal/load/pkg.go.
   387  type jsonPackage struct {
   388  	ImportPath        string
   389  	Dir               string
   390  	Name              string
   391  	Export            string
   392  	GoFiles           []string
   393  	CompiledGoFiles   []string
   394  	IgnoredGoFiles    []string
   395  	IgnoredOtherFiles []string
   396  	CFiles            []string
   397  	CgoFiles          []string
   398  	CXXFiles          []string
   399  	MFiles            []string
   400  	HFiles            []string
   401  	FFiles            []string
   402  	SFiles            []string
   403  	SwigFiles         []string
   404  	SwigCXXFiles      []string
   405  	SysoFiles         []string
   406  	Imports           []string
   407  	ImportMap         map[string]string
   408  	Deps              []string
   409  	Module            *Module
   410  	TestGoFiles       []string
   411  	TestImports       []string
   412  	XTestGoFiles      []string
   413  	XTestImports      []string
   414  	ForTest           string // q in a "p [q.test]" package, else ""
   415  	DepOnly           bool
   416  
   417  	Error      *packagesinternal.PackageError
   418  	DepsErrors []*packagesinternal.PackageError
   419  }
   420  
   421  type jsonPackageError struct {
   422  	ImportStack []string
   423  	Pos         string
   424  	Err         string
   425  }
   426  
   427  func otherFiles(p *jsonPackage) [][]string {
   428  	return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
   429  }
   430  
   431  // createDriverResponse uses the "go list" command to expand the pattern
   432  // words and return a response for the specified packages.
   433  func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) {
   434  	// go list uses the following identifiers in ImportPath and Imports:
   435  	//
   436  	// 	"p"			-- importable package or main (command)
   437  	// 	"q.test"		-- q's test executable
   438  	// 	"p [q.test]"		-- variant of p as built for q's test executable
   439  	// 	"q_test [q.test]"	-- q's external test package
   440  	//
   441  	// The packages p that are built differently for a test q.test
   442  	// are q itself, plus any helpers used by the external test q_test,
   443  	// typically including "testing" and all its dependencies.
   444  
   445  	// Run "go list" for complete
   446  	// information on the specified packages.
   447  	buf, err := state.invokeGo("list", golistargs(state.cfg, words)...)
   448  	if err != nil {
   449  		return nil, err
   450  	}
   451  	seen := make(map[string]*jsonPackage)
   452  	pkgs := make(map[string]*Package)
   453  	additionalErrors := make(map[string][]Error)
   454  	// Decode the JSON and convert it to Package form.
   455  	var response driverResponse
   456  	for dec := json.NewDecoder(buf); dec.More(); {
   457  		p := new(jsonPackage)
   458  		if err := dec.Decode(p); err != nil {
   459  			return nil, fmt.Errorf("JSON decoding failed: %v", err)
   460  		}
   461  
   462  		if p.ImportPath == "" {
   463  			// The documentation for go list says that “[e]rroneous packages will have
   464  			// a non-empty ImportPath”. If for some reason it comes back empty, we
   465  			// prefer to error out rather than silently discarding data or handing
   466  			// back a package without any way to refer to it.
   467  			if p.Error != nil {
   468  				return nil, Error{
   469  					Pos: p.Error.Pos,
   470  					Msg: p.Error.Err,
   471  				}
   472  			}
   473  			return nil, fmt.Errorf("package missing import path: %+v", p)
   474  		}
   475  
   476  		// Work around https://golang.org/issue/33157:
   477  		// go list -e, when given an absolute path, will find the package contained at
   478  		// that directory. But when no package exists there, it will return a fake package
   479  		// with an error and the ImportPath set to the absolute path provided to go list.
   480  		// Try to convert that absolute path to what its package path would be if it's
   481  		// contained in a known module or GOPATH entry. This will allow the package to be
   482  		// properly "reclaimed" when overlays are processed.
   483  		if filepath.IsAbs(p.ImportPath) && p.Error != nil {
   484  			pkgPath, ok, err := state.getPkgPath(p.ImportPath)
   485  			if err != nil {
   486  				return nil, err
   487  			}
   488  			if ok {
   489  				p.ImportPath = pkgPath
   490  			}
   491  		}
   492  
   493  		if old, found := seen[p.ImportPath]; found {
   494  			// If one version of the package has an error, and the other doesn't, assume
   495  			// that this is a case where go list is reporting a fake dependency variant
   496  			// of the imported package: When a package tries to invalidly import another
   497  			// package, go list emits a variant of the imported package (with the same
   498  			// import path, but with an error on it, and the package will have a
   499  			// DepError set on it). An example of when this can happen is for imports of
   500  			// main packages: main packages can not be imported, but they may be
   501  			// separately matched and listed by another pattern.
   502  			// See golang.org/issue/36188 for more details.
   503  
   504  			// The plan is that eventually, hopefully in Go 1.15, the error will be
   505  			// reported on the importing package rather than the duplicate "fake"
   506  			// version of the imported package. Once all supported versions of Go
   507  			// have the new behavior this logic can be deleted.
   508  			// TODO(matloob): delete the workaround logic once all supported versions of
   509  			// Go return the errors on the proper package.
   510  
   511  			// There should be exactly one version of a package that doesn't have an
   512  			// error.
   513  			if old.Error == nil && p.Error == nil {
   514  				if !reflect.DeepEqual(p, old) {
   515  					return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
   516  				}
   517  				continue
   518  			}
   519  
   520  			// Determine if this package's error needs to be bubbled up.
   521  			// This is a hack, and we expect for go list to eventually set the error
   522  			// on the package.
   523  			if old.Error != nil {
   524  				var errkind string
   525  				if strings.Contains(old.Error.Err, "not an importable package") {
   526  					errkind = "not an importable package"
   527  				} else if strings.Contains(old.Error.Err, "use of internal package") && strings.Contains(old.Error.Err, "not allowed") {
   528  					errkind = "use of internal package not allowed"
   529  				}
   530  				if errkind != "" {
   531  					if len(old.Error.ImportStack) < 1 {
   532  						return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, errkind)
   533  					}
   534  					importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-1]
   535  					if importingPkg == old.ImportPath {
   536  						// Using an older version of Go which put this package itself on top of import
   537  						// stack, instead of the importer. Look for importer in second from top
   538  						// position.
   539  						if len(old.Error.ImportStack) < 2 {
   540  							return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, errkind)
   541  						}
   542  						importingPkg = old.Error.ImportStack[len(old.Error.ImportStack)-2]
   543  					}
   544  					additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
   545  						Pos:  old.Error.Pos,
   546  						Msg:  old.Error.Err,
   547  						Kind: ListError,
   548  					})
   549  				}
   550  			}
   551  
   552  			// Make sure that if there's a version of the package without an error,
   553  			// that's the one reported to the user.
   554  			if old.Error == nil {
   555  				continue
   556  			}
   557  
   558  			// This package will replace the old one at the end of the loop.
   559  		}
   560  		seen[p.ImportPath] = p
   561  
   562  		pkg := &Package{
   563  			Name:            p.Name,
   564  			ID:              p.ImportPath,
   565  			GoFiles:         absJoin(p.Dir, p.GoFiles, p.CgoFiles),
   566  			CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
   567  			OtherFiles:      absJoin(p.Dir, otherFiles(p)...),
   568  			IgnoredFiles:    absJoin(p.Dir, p.IgnoredGoFiles, p.IgnoredOtherFiles),
   569  			forTest:         p.ForTest,
   570  			depsErrors:      p.DepsErrors,
   571  			Module:          p.Module,
   572  		}
   573  
   574  		if (state.cfg.Mode&typecheckCgo) != 0 && len(p.CgoFiles) != 0 {
   575  			if len(p.CompiledGoFiles) > len(p.GoFiles) {
   576  				// We need the cgo definitions, which are in the first
   577  				// CompiledGoFile after the non-cgo ones. This is a hack but there
   578  				// isn't currently a better way to find it. We also need the pure
   579  				// Go files and unprocessed cgo files, all of which are already
   580  				// in pkg.GoFiles.
   581  				cgoTypes := p.CompiledGoFiles[len(p.GoFiles)]
   582  				pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...)
   583  			} else {
   584  				// golang/go#38990: go list silently fails to do cgo processing
   585  				pkg.CompiledGoFiles = nil
   586  				pkg.Errors = append(pkg.Errors, Error{
   587  					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.",
   588  					Kind: ListError,
   589  				})
   590  			}
   591  		}
   592  
   593  		// Work around https://golang.org/issue/28749:
   594  		// cmd/go puts assembly, C, and C++ files in CompiledGoFiles.
   595  		// Filter out any elements of CompiledGoFiles that are also in OtherFiles.
   596  		// We have to keep this workaround in place until go1.12 is a distant memory.
   597  		if len(pkg.OtherFiles) > 0 {
   598  			other := make(map[string]bool, len(pkg.OtherFiles))
   599  			for _, f := range pkg.OtherFiles {
   600  				other[f] = true
   601  			}
   602  
   603  			out := pkg.CompiledGoFiles[:0]
   604  			for _, f := range pkg.CompiledGoFiles {
   605  				if other[f] {
   606  					continue
   607  				}
   608  				out = append(out, f)
   609  			}
   610  			pkg.CompiledGoFiles = out
   611  		}
   612  
   613  		// Extract the PkgPath from the package's ID.
   614  		if i := strings.IndexByte(pkg.ID, ' '); i >= 0 {
   615  			pkg.PkgPath = pkg.ID[:i]
   616  		} else {
   617  			pkg.PkgPath = pkg.ID
   618  		}
   619  
   620  		if pkg.PkgPath == "unsafe" {
   621  			pkg.GoFiles = nil // ignore fake unsafe.go file
   622  		}
   623  
   624  		// Assume go list emits only absolute paths for Dir.
   625  		if p.Dir != "" && !filepath.IsAbs(p.Dir) {
   626  			log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
   627  		}
   628  
   629  		if p.Export != "" && !filepath.IsAbs(p.Export) {
   630  			pkg.ExportFile = filepath.Join(p.Dir, p.Export)
   631  		} else {
   632  			pkg.ExportFile = p.Export
   633  		}
   634  
   635  		// imports
   636  		//
   637  		// Imports contains the IDs of all imported packages.
   638  		// ImportsMap records (path, ID) only where they differ.
   639  		ids := make(map[string]bool)
   640  		for _, id := range p.Imports {
   641  			ids[id] = true
   642  		}
   643  		pkg.Imports = make(map[string]*Package)
   644  		for path, id := range p.ImportMap {
   645  			pkg.Imports[path] = &Package{ID: id} // non-identity import
   646  			delete(ids, id)
   647  		}
   648  		for id := range ids {
   649  			if id == "C" {
   650  				continue
   651  			}
   652  
   653  			pkg.Imports[id] = &Package{ID: id} // identity import
   654  		}
   655  		if !p.DepOnly {
   656  			response.Roots = append(response.Roots, pkg.ID)
   657  		}
   658  
   659  		// Work around for pre-go.1.11 versions of go list.
   660  		// TODO(matloob): they should be handled by the fallback.
   661  		// Can we delete this?
   662  		if len(pkg.CompiledGoFiles) == 0 {
   663  			pkg.CompiledGoFiles = pkg.GoFiles
   664  		}
   665  
   666  		// Temporary work-around for golang/go#39986. Parse filenames out of
   667  		// error messages. This happens if there are unrecoverable syntax
   668  		// errors in the source, so we can't match on a specific error message.
   669  		if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
   670  			addFilenameFromPos := func(pos string) bool {
   671  				split := strings.Split(pos, ":")
   672  				if len(split) < 1 {
   673  					return false
   674  				}
   675  				filename := strings.TrimSpace(split[0])
   676  				if filename == "" {
   677  					return false
   678  				}
   679  				if !filepath.IsAbs(filename) {
   680  					filename = filepath.Join(state.cfg.Dir, filename)
   681  				}
   682  				info, _ := os.Stat(filename)
   683  				if info == nil {
   684  					return false
   685  				}
   686  				pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
   687  				pkg.GoFiles = append(pkg.GoFiles, filename)
   688  				return true
   689  			}
   690  			found := addFilenameFromPos(err.Pos)
   691  			// In some cases, go list only reports the error position in the
   692  			// error text, not the error position. One such case is when the
   693  			// file's package name is a keyword (see golang.org/issue/39763).
   694  			if !found {
   695  				addFilenameFromPos(err.Err)
   696  			}
   697  		}
   698  
   699  		if p.Error != nil {
   700  			msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
   701  			// Address golang.org/issue/35964 by appending import stack to error message.
   702  			if msg == "import cycle not allowed" && len(p.Error.ImportStack) != 0 {
   703  				msg += fmt.Sprintf(": import stack: %v", p.Error.ImportStack)
   704  			}
   705  			pkg.Errors = append(pkg.Errors, Error{
   706  				Pos:  p.Error.Pos,
   707  				Msg:  msg,
   708  				Kind: ListError,
   709  			})
   710  		}
   711  
   712  		pkgs[pkg.ID] = pkg
   713  	}
   714  
   715  	for id, errs := range additionalErrors {
   716  		if p, ok := pkgs[id]; ok {
   717  			p.Errors = append(p.Errors, errs...)
   718  		}
   719  	}
   720  	for _, pkg := range pkgs {
   721  		response.Packages = append(response.Packages, pkg)
   722  	}
   723  	sort.Slice(response.Packages, func(i, j int) bool { return response.Packages[i].ID < response.Packages[j].ID })
   724  
   725  	return &response, nil
   726  }
   727  
   728  func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool {
   729  	if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 {
   730  		return false
   731  	}
   732  
   733  	goV, err := state.getGoVersion()
   734  	if err != nil {
   735  		return false
   736  	}
   737  
   738  	// On Go 1.14 and earlier, only add filenames from errors if the import stack is empty.
   739  	// The import stack behaves differently for these versions than newer Go versions.
   740  	if goV < 15 {
   741  		return len(p.Error.ImportStack) == 0
   742  	}
   743  
   744  	// On Go 1.15 and later, only parse filenames out of error if there's no import stack,
   745  	// or the current package is at the top of the import stack. This is not guaranteed
   746  	// to work perfectly, but should avoid some cases where files in errors don't belong to this
   747  	// package.
   748  	return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath
   749  }
   750  
   751  func (state *golistState) getGoVersion() (int, error) {
   752  	state.goVersionOnce.Do(func() {
   753  		state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.cfg.gocmdRunner)
   754  	})
   755  	return state.goVersion, state.goVersionError
   756  }
   757  
   758  // getPkgPath finds the package path of a directory if it's relative to a root
   759  // directory.
   760  func (state *golistState) getPkgPath(dir string) (string, bool, error) {
   761  	absDir, err := filepath.Abs(dir)
   762  	if err != nil {
   763  		return "", false, err
   764  	}
   765  	roots, err := state.determineRootDirs()
   766  	if err != nil {
   767  		return "", false, err
   768  	}
   769  
   770  	for rdir, rpath := range roots {
   771  		// Make sure that the directory is in the module,
   772  		// to avoid creating a path relative to another module.
   773  		if !strings.HasPrefix(absDir, rdir) {
   774  			continue
   775  		}
   776  		// TODO(matloob): This doesn't properly handle symlinks.
   777  		r, err := filepath.Rel(rdir, dir)
   778  		if err != nil {
   779  			continue
   780  		}
   781  		if rpath != "" {
   782  			// We choose only one root even though the directory even it can belong in multiple modules
   783  			// or GOPATH entries. This is okay because we only need to work with absolute dirs when a
   784  			// file is missing from disk, for instance when gopls calls go/packages in an overlay.
   785  			// Once the file is saved, gopls, or the next invocation of the tool will get the correct
   786  			// result straight from golist.
   787  			// TODO(matloob): Implement module tiebreaking?
   788  			return path.Join(rpath, filepath.ToSlash(r)), true, nil
   789  		}
   790  		return filepath.ToSlash(r), true, nil
   791  	}
   792  	return "", false, nil
   793  }
   794  
   795  // absJoin absolutizes and flattens the lists of files.
   796  func absJoin(dir string, fileses ...[]string) (res []string) {
   797  	for _, files := range fileses {
   798  		for _, file := range files {
   799  			if !filepath.IsAbs(file) {
   800  				file = filepath.Join(dir, file)
   801  			}
   802  			res = append(res, file)
   803  		}
   804  	}
   805  	return res
   806  }
   807  
   808  func golistargs(cfg *Config, words []string) []string {
   809  	const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
   810  	fullargs := []string{
   811  		"-e", "-json",
   812  		fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),
   813  		fmt.Sprintf("-test=%t", cfg.Tests),
   814  		fmt.Sprintf("-export=%t", usesExportData(cfg)),
   815  		fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
   816  		// go list doesn't let you pass -test and -find together,
   817  		// probably because you'd just get the TestMain.
   818  		fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0),
   819  	}
   820  	fullargs = append(fullargs, cfg.BuildFlags...)
   821  	fullargs = append(fullargs, "--")
   822  	fullargs = append(fullargs, words...)
   823  	return fullargs
   824  }
   825  
   826  // cfgInvocation returns an Invocation that reflects cfg's settings.
   827  func (state *golistState) cfgInvocation() gocommand.Invocation {
   828  	cfg := state.cfg
   829  	return gocommand.Invocation{
   830  		BuildFlags: cfg.BuildFlags,
   831  		ModFile:    cfg.modFile,
   832  		ModFlag:    cfg.modFlag,
   833  		CleanEnv:   cfg.Env != nil,
   834  		Env:        cfg.Env,
   835  		Logf:       cfg.Logf,
   836  		WorkingDir: cfg.Dir,
   837  	}
   838  }
   839  
   840  // invokeGo returns the stdout of a go command invocation.
   841  func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
   842  	cfg := state.cfg
   843  
   844  	inv := state.cfgInvocation()
   845  
   846  	// For Go versions 1.16 and above, `go list` accepts overlays directly via
   847  	// the -overlay flag. Set it, if it's available.
   848  	//
   849  	// The check for "list" is not necessarily required, but we should avoid
   850  	// getting the go version if possible.
   851  	if verb == "list" {
   852  		goVersion, err := state.getGoVersion()
   853  		if err != nil {
   854  			return nil, err
   855  		}
   856  		if goVersion >= 16 {
   857  			filename, cleanup, err := state.writeOverlays()
   858  			if err != nil {
   859  				return nil, err
   860  			}
   861  			defer cleanup()
   862  			inv.Overlay = filename
   863  		}
   864  	}
   865  	inv.Verb = verb
   866  	inv.Args = args
   867  	gocmdRunner := cfg.gocmdRunner
   868  	if gocmdRunner == nil {
   869  		gocmdRunner = &gocommand.Runner{}
   870  	}
   871  	stdout, stderr, friendlyErr, err := gocmdRunner.RunRaw(cfg.Context, inv)
   872  	if err != nil {
   873  		// Check for 'go' executable not being found.
   874  		if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
   875  			return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
   876  		}
   877  
   878  		exitErr, ok := err.(*exec.ExitError)
   879  		if !ok {
   880  			// Catastrophic error:
   881  			// - context cancellation
   882  			return nil, xerrors.Errorf("couldn't run 'go': %w", err)
   883  		}
   884  
   885  		// Old go version?
   886  		if strings.Contains(stderr.String(), "flag provided but not defined") {
   887  			return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)}
   888  		}
   889  
   890  		// Related to #24854
   891  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "unexpected directory layout") {
   892  			return nil, friendlyErr
   893  		}
   894  
   895  		// Is there an error running the C compiler in cgo? This will be reported in the "Error" field
   896  		// and should be suppressed by go list -e.
   897  		//
   898  		// This condition is not perfect yet because the error message can include other error messages than runtime/cgo.
   899  		isPkgPathRune := func(r rune) bool {
   900  			// From https://golang.org/ref/spec#Import_declarations:
   901  			//    Implementation restriction: A compiler may restrict ImportPaths to non-empty strings
   902  			//    using only characters belonging to Unicode's L, M, N, P, and S general categories
   903  			//    (the Graphic characters without spaces) and may also exclude the
   904  			//    characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD.
   905  			return unicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, r) &&
   906  				!strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r)
   907  		}
   908  		// golang/go#36770: Handle case where cmd/go prints module download messages before the error.
   909  		msg := stderr.String()
   910  		for strings.HasPrefix(msg, "go: downloading") {
   911  			msg = msg[strings.IndexRune(msg, '\n')+1:]
   912  		}
   913  		if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") {
   914  			msg := msg[len("# "):]
   915  			if strings.HasPrefix(strings.TrimLeftFunc(msg, isPkgPathRune), "\n") {
   916  				return stdout, nil
   917  			}
   918  			// Treat pkg-config errors as a special case (golang.org/issue/36770).
   919  			if strings.HasPrefix(msg, "pkg-config") {
   920  				return stdout, nil
   921  			}
   922  		}
   923  
   924  		// This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show
   925  		// the error in the Err section of stdout in case -e option is provided.
   926  		// This fix is provided for backwards compatibility.
   927  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must be .go files") {
   928  			output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   929  				strings.Trim(stderr.String(), "\n"))
   930  			return bytes.NewBufferString(output), nil
   931  		}
   932  
   933  		// Similar to the previous error, but currently lacks a fix in Go.
   934  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must all be in one directory") {
   935  			output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   936  				strings.Trim(stderr.String(), "\n"))
   937  			return bytes.NewBufferString(output), nil
   938  		}
   939  
   940  		// Backwards compatibility for Go 1.11 because 1.12 and 1.13 put the directory in the ImportPath.
   941  		// If the package doesn't exist, put the absolute path of the directory into the error message,
   942  		// as Go 1.13 list does.
   943  		const noSuchDirectory = "no such directory"
   944  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), noSuchDirectory) {
   945  			errstr := stderr.String()
   946  			abspath := strings.TrimSpace(errstr[strings.Index(errstr, noSuchDirectory)+len(noSuchDirectory):])
   947  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   948  				abspath, strings.Trim(stderr.String(), "\n"))
   949  			return bytes.NewBufferString(output), nil
   950  		}
   951  
   952  		// Workaround for #29280: go list -e has incorrect behavior when an ad-hoc package doesn't exist.
   953  		// Note that the error message we look for in this case is different that the one looked for above.
   954  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no such file or directory") {
   955  			output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   956  				strings.Trim(stderr.String(), "\n"))
   957  			return bytes.NewBufferString(output), nil
   958  		}
   959  
   960  		// Workaround for #34273. go list -e with GO111MODULE=on has incorrect behavior when listing a
   961  		// directory outside any module.
   962  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside available modules") {
   963  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   964  				// TODO(matloob): command-line-arguments isn't correct here.
   965  				"command-line-arguments", strings.Trim(stderr.String(), "\n"))
   966  			return bytes.NewBufferString(output), nil
   967  		}
   968  
   969  		// Another variation of the previous error
   970  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside module root") {
   971  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   972  				// TODO(matloob): command-line-arguments isn't correct here.
   973  				"command-line-arguments", strings.Trim(stderr.String(), "\n"))
   974  			return bytes.NewBufferString(output), nil
   975  		}
   976  
   977  		// Workaround for an instance of golang.org/issue/26755: go list -e  will return a non-zero exit
   978  		// status if there's a dependency on a package that doesn't exist. But it should return
   979  		// a zero exit status and set an error on that package.
   980  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no Go files in") {
   981  			// Don't clobber stdout if `go list` actually returned something.
   982  			if len(stdout.String()) > 0 {
   983  				return stdout, nil
   984  			}
   985  			// try to extract package name from string
   986  			stderrStr := stderr.String()
   987  			var importPath string
   988  			colon := strings.Index(stderrStr, ":")
   989  			if colon > 0 && strings.HasPrefix(stderrStr, "go build ") {
   990  				importPath = stderrStr[len("go build "):colon]
   991  			}
   992  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   993  				importPath, strings.Trim(stderrStr, "\n"))
   994  			return bytes.NewBufferString(output), nil
   995  		}
   996  
   997  		// Export mode entails a build.
   998  		// If that build fails, errors appear on stderr
   999  		// (despite the -e flag) and the Export field is blank.
  1000  		// Do not fail in that case.
  1001  		// The same is true if an ad-hoc package given to go list doesn't exist.
  1002  		// TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when
  1003  		// packages don't exist or a build fails.
  1004  		if !usesExportData(cfg) && !containsGoFile(args) {
  1005  			return nil, friendlyErr
  1006  		}
  1007  	}
  1008  	return stdout, nil
  1009  }
  1010  
  1011  // OverlayJSON is the format overlay files are expected to be in.
  1012  // The Replace map maps from overlaid paths to replacement paths:
  1013  // the Go command will forward all reads trying to open
  1014  // each overlaid path to its replacement path, or consider the overlaid
  1015  // path not to exist if the replacement path is empty.
  1016  //
  1017  // From golang/go#39958.
  1018  type OverlayJSON struct {
  1019  	Replace map[string]string `json:"replace,omitempty"`
  1020  }
  1021  
  1022  // writeOverlays writes out files for go list's -overlay flag, as described
  1023  // above.
  1024  func (state *golistState) writeOverlays() (filename string, cleanup func(), err error) {
  1025  	// Do nothing if there are no overlays in the config.
  1026  	if len(state.cfg.Overlay) == 0 {
  1027  		return "", func() {}, nil
  1028  	}
  1029  	dir, err := ioutil.TempDir("", "gopackages-*")
  1030  	if err != nil {
  1031  		return "", nil, err
  1032  	}
  1033  	// The caller must clean up this directory, unless this function returns an
  1034  	// error.
  1035  	cleanup = func() {
  1036  		os.RemoveAll(dir)
  1037  	}
  1038  	defer func() {
  1039  		if err != nil {
  1040  			cleanup()
  1041  		}
  1042  	}()
  1043  	overlays := map[string]string{}
  1044  	for k, v := range state.cfg.Overlay {
  1045  		// Create a unique filename for the overlaid files, to avoid
  1046  		// creating nested directories.
  1047  		noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
  1048  		f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator))
  1049  		if err != nil {
  1050  			return "", func() {}, err
  1051  		}
  1052  		if _, err := f.Write(v); err != nil {
  1053  			return "", func() {}, err
  1054  		}
  1055  		if err := f.Close(); err != nil {
  1056  			return "", func() {}, err
  1057  		}
  1058  		overlays[k] = f.Name()
  1059  	}
  1060  	b, err := json.Marshal(OverlayJSON{Replace: overlays})
  1061  	if err != nil {
  1062  		return "", func() {}, err
  1063  	}
  1064  	// Write out the overlay file that contains the filepath mappings.
  1065  	filename = filepath.Join(dir, "overlay.json")
  1066  	if err := ioutil.WriteFile(filename, b, 0665); err != nil {
  1067  		return "", func() {}, err
  1068  	}
  1069  	return filename, cleanup, nil
  1070  }
  1071  
  1072  func containsGoFile(s []string) bool {
  1073  	for _, f := range s {
  1074  		if strings.HasSuffix(f, ".go") {
  1075  			return true
  1076  		}
  1077  	}
  1078  	return false
  1079  }
  1080  
  1081  func cmdDebugStr(cmd *exec.Cmd) string {
  1082  	env := make(map[string]string)
  1083  	for _, kv := range cmd.Env {
  1084  		split := strings.SplitN(kv, "=", 2)
  1085  		k, v := split[0], split[1]
  1086  		env[k] = v
  1087  	}
  1088  
  1089  	var args []string
  1090  	for _, arg := range cmd.Args {
  1091  		quoted := strconv.Quote(arg)
  1092  		if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") {
  1093  			args = append(args, quoted)
  1094  		} else {
  1095  			args = append(args, arg)
  1096  		}
  1097  	}
  1098  	return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " "))
  1099  }