github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/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/powerman/golang-tools/go/internal/packagesdriver"
    27  	"github.com/powerman/golang-tools/internal/gocommand"
    28  	"github.com/powerman/golang-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  	EmbedPatterns     []string
   397  	EmbedFiles        []string
   398  	CFiles            []string
   399  	CgoFiles          []string
   400  	CXXFiles          []string
   401  	MFiles            []string
   402  	HFiles            []string
   403  	FFiles            []string
   404  	SFiles            []string
   405  	SwigFiles         []string
   406  	SwigCXXFiles      []string
   407  	SysoFiles         []string
   408  	Imports           []string
   409  	ImportMap         map[string]string
   410  	Deps              []string
   411  	Module            *Module
   412  	TestGoFiles       []string
   413  	TestImports       []string
   414  	XTestGoFiles      []string
   415  	XTestImports      []string
   416  	ForTest           string // q in a "p [q.test]" package, else ""
   417  	DepOnly           bool
   418  
   419  	Error      *packagesinternal.PackageError
   420  	DepsErrors []*packagesinternal.PackageError
   421  }
   422  
   423  type jsonPackageError struct {
   424  	ImportStack []string
   425  	Pos         string
   426  	Err         string
   427  }
   428  
   429  func otherFiles(p *jsonPackage) [][]string {
   430  	return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
   431  }
   432  
   433  // createDriverResponse uses the "go list" command to expand the pattern
   434  // words and return a response for the specified packages.
   435  func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) {
   436  	// go list uses the following identifiers in ImportPath and Imports:
   437  	//
   438  	// 	"p"			-- importable package or main (command)
   439  	// 	"q.test"		-- q's test executable
   440  	// 	"p [q.test]"		-- variant of p as built for q's test executable
   441  	// 	"q_test [q.test]"	-- q's external test package
   442  	//
   443  	// The packages p that are built differently for a test q.test
   444  	// are q itself, plus any helpers used by the external test q_test,
   445  	// typically including "testing" and all its dependencies.
   446  
   447  	// Run "go list" for complete
   448  	// information on the specified packages.
   449  	buf, err := state.invokeGo("list", golistargs(state.cfg, words)...)
   450  	if err != nil {
   451  		return nil, err
   452  	}
   453  	seen := make(map[string]*jsonPackage)
   454  	pkgs := make(map[string]*Package)
   455  	additionalErrors := make(map[string][]Error)
   456  	// Decode the JSON and convert it to Package form.
   457  	var response driverResponse
   458  	for dec := json.NewDecoder(buf); dec.More(); {
   459  		p := new(jsonPackage)
   460  		if err := dec.Decode(p); err != nil {
   461  			return nil, fmt.Errorf("JSON decoding failed: %v", err)
   462  		}
   463  
   464  		if p.ImportPath == "" {
   465  			// The documentation for go list says that “[e]rroneous packages will have
   466  			// a non-empty ImportPath”. If for some reason it comes back empty, we
   467  			// prefer to error out rather than silently discarding data or handing
   468  			// back a package without any way to refer to it.
   469  			if p.Error != nil {
   470  				return nil, Error{
   471  					Pos: p.Error.Pos,
   472  					Msg: p.Error.Err,
   473  				}
   474  			}
   475  			return nil, fmt.Errorf("package missing import path: %+v", p)
   476  		}
   477  
   478  		// Work around https://golang.org/issue/33157:
   479  		// go list -e, when given an absolute path, will find the package contained at
   480  		// that directory. But when no package exists there, it will return a fake package
   481  		// with an error and the ImportPath set to the absolute path provided to go list.
   482  		// Try to convert that absolute path to what its package path would be if it's
   483  		// contained in a known module or GOPATH entry. This will allow the package to be
   484  		// properly "reclaimed" when overlays are processed.
   485  		if filepath.IsAbs(p.ImportPath) && p.Error != nil {
   486  			pkgPath, ok, err := state.getPkgPath(p.ImportPath)
   487  			if err != nil {
   488  				return nil, err
   489  			}
   490  			if ok {
   491  				p.ImportPath = pkgPath
   492  			}
   493  		}
   494  
   495  		if old, found := seen[p.ImportPath]; found {
   496  			// If one version of the package has an error, and the other doesn't, assume
   497  			// that this is a case where go list is reporting a fake dependency variant
   498  			// of the imported package: When a package tries to invalidly import another
   499  			// package, go list emits a variant of the imported package (with the same
   500  			// import path, but with an error on it, and the package will have a
   501  			// DepError set on it). An example of when this can happen is for imports of
   502  			// main packages: main packages can not be imported, but they may be
   503  			// separately matched and listed by another pattern.
   504  			// See golang.org/issue/36188 for more details.
   505  
   506  			// The plan is that eventually, hopefully in Go 1.15, the error will be
   507  			// reported on the importing package rather than the duplicate "fake"
   508  			// version of the imported package. Once all supported versions of Go
   509  			// have the new behavior this logic can be deleted.
   510  			// TODO(matloob): delete the workaround logic once all supported versions of
   511  			// Go return the errors on the proper package.
   512  
   513  			// There should be exactly one version of a package that doesn't have an
   514  			// error.
   515  			if old.Error == nil && p.Error == nil {
   516  				if !reflect.DeepEqual(p, old) {
   517  					return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
   518  				}
   519  				continue
   520  			}
   521  
   522  			// Determine if this package's error needs to be bubbled up.
   523  			// This is a hack, and we expect for go list to eventually set the error
   524  			// on the package.
   525  			if old.Error != nil {
   526  				var errkind string
   527  				if strings.Contains(old.Error.Err, "not an importable package") {
   528  					errkind = "not an importable package"
   529  				} else if strings.Contains(old.Error.Err, "use of internal package") && strings.Contains(old.Error.Err, "not allowed") {
   530  					errkind = "use of internal package not allowed"
   531  				}
   532  				if errkind != "" {
   533  					if len(old.Error.ImportStack) < 1 {
   534  						return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, errkind)
   535  					}
   536  					importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-1]
   537  					if importingPkg == old.ImportPath {
   538  						// Using an older version of Go which put this package itself on top of import
   539  						// stack, instead of the importer. Look for importer in second from top
   540  						// position.
   541  						if len(old.Error.ImportStack) < 2 {
   542  							return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, errkind)
   543  						}
   544  						importingPkg = old.Error.ImportStack[len(old.Error.ImportStack)-2]
   545  					}
   546  					additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
   547  						Pos:  old.Error.Pos,
   548  						Msg:  old.Error.Err,
   549  						Kind: ListError,
   550  					})
   551  				}
   552  			}
   553  
   554  			// Make sure that if there's a version of the package without an error,
   555  			// that's the one reported to the user.
   556  			if old.Error == nil {
   557  				continue
   558  			}
   559  
   560  			// This package will replace the old one at the end of the loop.
   561  		}
   562  		seen[p.ImportPath] = p
   563  
   564  		pkg := &Package{
   565  			Name:            p.Name,
   566  			ID:              p.ImportPath,
   567  			GoFiles:         absJoin(p.Dir, p.GoFiles, p.CgoFiles),
   568  			CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
   569  			OtherFiles:      absJoin(p.Dir, otherFiles(p)...),
   570  			EmbedFiles:      absJoin(p.Dir, p.EmbedFiles),
   571  			EmbedPatterns:   absJoin(p.Dir, p.EmbedPatterns),
   572  			IgnoredFiles:    absJoin(p.Dir, p.IgnoredGoFiles, p.IgnoredOtherFiles),
   573  			forTest:         p.ForTest,
   574  			depsErrors:      p.DepsErrors,
   575  			Module:          p.Module,
   576  		}
   577  
   578  		if (state.cfg.Mode&typecheckCgo) != 0 && len(p.CgoFiles) != 0 {
   579  			if len(p.CompiledGoFiles) > len(p.GoFiles) {
   580  				// We need the cgo definitions, which are in the first
   581  				// CompiledGoFile after the non-cgo ones. This is a hack but there
   582  				// isn't currently a better way to find it. We also need the pure
   583  				// Go files and unprocessed cgo files, all of which are already
   584  				// in pkg.GoFiles.
   585  				cgoTypes := p.CompiledGoFiles[len(p.GoFiles)]
   586  				pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...)
   587  			} else {
   588  				// golang/go#38990: go list silently fails to do cgo processing
   589  				pkg.CompiledGoFiles = nil
   590  				pkg.Errors = append(pkg.Errors, Error{
   591  					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.",
   592  					Kind: ListError,
   593  				})
   594  			}
   595  		}
   596  
   597  		// Work around https://golang.org/issue/28749:
   598  		// cmd/go puts assembly, C, and C++ files in CompiledGoFiles.
   599  		// Filter out any elements of CompiledGoFiles that are also in OtherFiles.
   600  		// We have to keep this workaround in place until go1.12 is a distant memory.
   601  		if len(pkg.OtherFiles) > 0 {
   602  			other := make(map[string]bool, len(pkg.OtherFiles))
   603  			for _, f := range pkg.OtherFiles {
   604  				other[f] = true
   605  			}
   606  
   607  			out := pkg.CompiledGoFiles[:0]
   608  			for _, f := range pkg.CompiledGoFiles {
   609  				if other[f] {
   610  					continue
   611  				}
   612  				out = append(out, f)
   613  			}
   614  			pkg.CompiledGoFiles = out
   615  		}
   616  
   617  		// Extract the PkgPath from the package's ID.
   618  		if i := strings.IndexByte(pkg.ID, ' '); i >= 0 {
   619  			pkg.PkgPath = pkg.ID[:i]
   620  		} else {
   621  			pkg.PkgPath = pkg.ID
   622  		}
   623  
   624  		if pkg.PkgPath == "unsafe" {
   625  			pkg.GoFiles = nil // ignore fake unsafe.go file
   626  		}
   627  
   628  		// Assume go list emits only absolute paths for Dir.
   629  		if p.Dir != "" && !filepath.IsAbs(p.Dir) {
   630  			log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
   631  		}
   632  
   633  		if p.Export != "" && !filepath.IsAbs(p.Export) {
   634  			pkg.ExportFile = filepath.Join(p.Dir, p.Export)
   635  		} else {
   636  			pkg.ExportFile = p.Export
   637  		}
   638  
   639  		// imports
   640  		//
   641  		// Imports contains the IDs of all imported packages.
   642  		// ImportsMap records (path, ID) only where they differ.
   643  		ids := make(map[string]bool)
   644  		for _, id := range p.Imports {
   645  			ids[id] = true
   646  		}
   647  		pkg.Imports = make(map[string]*Package)
   648  		for path, id := range p.ImportMap {
   649  			pkg.Imports[path] = &Package{ID: id} // non-identity import
   650  			delete(ids, id)
   651  		}
   652  		for id := range ids {
   653  			if id == "C" {
   654  				continue
   655  			}
   656  
   657  			pkg.Imports[id] = &Package{ID: id} // identity import
   658  		}
   659  		if !p.DepOnly {
   660  			response.Roots = append(response.Roots, pkg.ID)
   661  		}
   662  
   663  		// Work around for pre-go.1.11 versions of go list.
   664  		// TODO(matloob): they should be handled by the fallback.
   665  		// Can we delete this?
   666  		if len(pkg.CompiledGoFiles) == 0 {
   667  			pkg.CompiledGoFiles = pkg.GoFiles
   668  		}
   669  
   670  		// Temporary work-around for golang/go#39986. Parse filenames out of
   671  		// error messages. This happens if there are unrecoverable syntax
   672  		// errors in the source, so we can't match on a specific error message.
   673  		if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
   674  			addFilenameFromPos := func(pos string) bool {
   675  				split := strings.Split(pos, ":")
   676  				if len(split) < 1 {
   677  					return false
   678  				}
   679  				filename := strings.TrimSpace(split[0])
   680  				if filename == "" {
   681  					return false
   682  				}
   683  				if !filepath.IsAbs(filename) {
   684  					filename = filepath.Join(state.cfg.Dir, filename)
   685  				}
   686  				info, _ := os.Stat(filename)
   687  				if info == nil {
   688  					return false
   689  				}
   690  				pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
   691  				pkg.GoFiles = append(pkg.GoFiles, filename)
   692  				return true
   693  			}
   694  			found := addFilenameFromPos(err.Pos)
   695  			// In some cases, go list only reports the error position in the
   696  			// error text, not the error position. One such case is when the
   697  			// file's package name is a keyword (see golang.org/issue/39763).
   698  			if !found {
   699  				addFilenameFromPos(err.Err)
   700  			}
   701  		}
   702  
   703  		if p.Error != nil {
   704  			msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
   705  			// Address golang.org/issue/35964 by appending import stack to error message.
   706  			if msg == "import cycle not allowed" && len(p.Error.ImportStack) != 0 {
   707  				msg += fmt.Sprintf(": import stack: %v", p.Error.ImportStack)
   708  			}
   709  			pkg.Errors = append(pkg.Errors, Error{
   710  				Pos:  p.Error.Pos,
   711  				Msg:  msg,
   712  				Kind: ListError,
   713  			})
   714  		}
   715  
   716  		pkgs[pkg.ID] = pkg
   717  	}
   718  
   719  	for id, errs := range additionalErrors {
   720  		if p, ok := pkgs[id]; ok {
   721  			p.Errors = append(p.Errors, errs...)
   722  		}
   723  	}
   724  	for _, pkg := range pkgs {
   725  		response.Packages = append(response.Packages, pkg)
   726  	}
   727  	sort.Slice(response.Packages, func(i, j int) bool { return response.Packages[i].ID < response.Packages[j].ID })
   728  
   729  	return &response, nil
   730  }
   731  
   732  func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool {
   733  	if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 {
   734  		return false
   735  	}
   736  
   737  	goV, err := state.getGoVersion()
   738  	if err != nil {
   739  		return false
   740  	}
   741  
   742  	// On Go 1.14 and earlier, only add filenames from errors if the import stack is empty.
   743  	// The import stack behaves differently for these versions than newer Go versions.
   744  	if goV < 15 {
   745  		return len(p.Error.ImportStack) == 0
   746  	}
   747  
   748  	// On Go 1.15 and later, only parse filenames out of error if there's no import stack,
   749  	// or the current package is at the top of the import stack. This is not guaranteed
   750  	// to work perfectly, but should avoid some cases where files in errors don't belong to this
   751  	// package.
   752  	return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath
   753  }
   754  
   755  func (state *golistState) getGoVersion() (int, error) {
   756  	state.goVersionOnce.Do(func() {
   757  		state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.cfg.gocmdRunner)
   758  	})
   759  	return state.goVersion, state.goVersionError
   760  }
   761  
   762  // getPkgPath finds the package path of a directory if it's relative to a root
   763  // directory.
   764  func (state *golistState) getPkgPath(dir string) (string, bool, error) {
   765  	absDir, err := filepath.Abs(dir)
   766  	if err != nil {
   767  		return "", false, err
   768  	}
   769  	roots, err := state.determineRootDirs()
   770  	if err != nil {
   771  		return "", false, err
   772  	}
   773  
   774  	for rdir, rpath := range roots {
   775  		// Make sure that the directory is in the module,
   776  		// to avoid creating a path relative to another module.
   777  		if !strings.HasPrefix(absDir, rdir) {
   778  			continue
   779  		}
   780  		// TODO(matloob): This doesn't properly handle symlinks.
   781  		r, err := filepath.Rel(rdir, dir)
   782  		if err != nil {
   783  			continue
   784  		}
   785  		if rpath != "" {
   786  			// We choose only one root even though the directory even it can belong in multiple modules
   787  			// or GOPATH entries. This is okay because we only need to work with absolute dirs when a
   788  			// file is missing from disk, for instance when gopls calls go/packages in an overlay.
   789  			// Once the file is saved, gopls, or the next invocation of the tool will get the correct
   790  			// result straight from golist.
   791  			// TODO(matloob): Implement module tiebreaking?
   792  			return path.Join(rpath, filepath.ToSlash(r)), true, nil
   793  		}
   794  		return filepath.ToSlash(r), true, nil
   795  	}
   796  	return "", false, nil
   797  }
   798  
   799  // absJoin absolutizes and flattens the lists of files.
   800  func absJoin(dir string, fileses ...[]string) (res []string) {
   801  	for _, files := range fileses {
   802  		for _, file := range files {
   803  			if !filepath.IsAbs(file) {
   804  				file = filepath.Join(dir, file)
   805  			}
   806  			res = append(res, file)
   807  		}
   808  	}
   809  	return res
   810  }
   811  
   812  func golistargs(cfg *Config, words []string) []string {
   813  	const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
   814  	fullargs := []string{
   815  		"-e", "-json",
   816  		fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),
   817  		fmt.Sprintf("-test=%t", cfg.Tests),
   818  		fmt.Sprintf("-export=%t", usesExportData(cfg)),
   819  		fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
   820  		// go list doesn't let you pass -test and -find together,
   821  		// probably because you'd just get the TestMain.
   822  		fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0 && !usesExportData(cfg)),
   823  	}
   824  	fullargs = append(fullargs, cfg.BuildFlags...)
   825  	fullargs = append(fullargs, "--")
   826  	fullargs = append(fullargs, words...)
   827  	return fullargs
   828  }
   829  
   830  // cfgInvocation returns an Invocation that reflects cfg's settings.
   831  func (state *golistState) cfgInvocation() gocommand.Invocation {
   832  	cfg := state.cfg
   833  	return gocommand.Invocation{
   834  		BuildFlags: cfg.BuildFlags,
   835  		ModFile:    cfg.modFile,
   836  		ModFlag:    cfg.modFlag,
   837  		CleanEnv:   cfg.Env != nil,
   838  		Env:        cfg.Env,
   839  		Logf:       cfg.Logf,
   840  		WorkingDir: cfg.Dir,
   841  	}
   842  }
   843  
   844  // invokeGo returns the stdout of a go command invocation.
   845  func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
   846  	cfg := state.cfg
   847  
   848  	inv := state.cfgInvocation()
   849  
   850  	// For Go versions 1.16 and above, `go list` accepts overlays directly via
   851  	// the -overlay flag. Set it, if it's available.
   852  	//
   853  	// The check for "list" is not necessarily required, but we should avoid
   854  	// getting the go version if possible.
   855  	if verb == "list" {
   856  		goVersion, err := state.getGoVersion()
   857  		if err != nil {
   858  			return nil, err
   859  		}
   860  		if goVersion >= 16 {
   861  			filename, cleanup, err := state.writeOverlays()
   862  			if err != nil {
   863  				return nil, err
   864  			}
   865  			defer cleanup()
   866  			inv.Overlay = filename
   867  		}
   868  	}
   869  	inv.Verb = verb
   870  	inv.Args = args
   871  	gocmdRunner := cfg.gocmdRunner
   872  	if gocmdRunner == nil {
   873  		gocmdRunner = &gocommand.Runner{}
   874  	}
   875  	stdout, stderr, friendlyErr, err := gocmdRunner.RunRaw(cfg.Context, inv)
   876  	if err != nil {
   877  		// Check for 'go' executable not being found.
   878  		if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
   879  			return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
   880  		}
   881  
   882  		exitErr, ok := err.(*exec.ExitError)
   883  		if !ok {
   884  			// Catastrophic error:
   885  			// - context cancellation
   886  			return nil, xerrors.Errorf("couldn't run 'go': %w", err)
   887  		}
   888  
   889  		// Old go version?
   890  		if strings.Contains(stderr.String(), "flag provided but not defined") {
   891  			return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)}
   892  		}
   893  
   894  		// Related to #24854
   895  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "unexpected directory layout") {
   896  			return nil, friendlyErr
   897  		}
   898  
   899  		// Is there an error running the C compiler in cgo? This will be reported in the "Error" field
   900  		// and should be suppressed by go list -e.
   901  		//
   902  		// This condition is not perfect yet because the error message can include other error messages than runtime/cgo.
   903  		isPkgPathRune := func(r rune) bool {
   904  			// From https://golang.org/ref/spec#Import_declarations:
   905  			//    Implementation restriction: A compiler may restrict ImportPaths to non-empty strings
   906  			//    using only characters belonging to Unicode's L, M, N, P, and S general categories
   907  			//    (the Graphic characters without spaces) and may also exclude the
   908  			//    characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD.
   909  			return unicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, r) &&
   910  				!strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r)
   911  		}
   912  		// golang/go#36770: Handle case where cmd/go prints module download messages before the error.
   913  		msg := stderr.String()
   914  		for strings.HasPrefix(msg, "go: downloading") {
   915  			msg = msg[strings.IndexRune(msg, '\n')+1:]
   916  		}
   917  		if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") {
   918  			msg := msg[len("# "):]
   919  			if strings.HasPrefix(strings.TrimLeftFunc(msg, isPkgPathRune), "\n") {
   920  				return stdout, nil
   921  			}
   922  			// Treat pkg-config errors as a special case (golang.org/issue/36770).
   923  			if strings.HasPrefix(msg, "pkg-config") {
   924  				return stdout, nil
   925  			}
   926  		}
   927  
   928  		// This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show
   929  		// the error in the Err section of stdout in case -e option is provided.
   930  		// This fix is provided for backwards compatibility.
   931  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must be .go files") {
   932  			output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   933  				strings.Trim(stderr.String(), "\n"))
   934  			return bytes.NewBufferString(output), nil
   935  		}
   936  
   937  		// Similar to the previous error, but currently lacks a fix in Go.
   938  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must all be in one directory") {
   939  			output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   940  				strings.Trim(stderr.String(), "\n"))
   941  			return bytes.NewBufferString(output), nil
   942  		}
   943  
   944  		// Backwards compatibility for Go 1.11 because 1.12 and 1.13 put the directory in the ImportPath.
   945  		// If the package doesn't exist, put the absolute path of the directory into the error message,
   946  		// as Go 1.13 list does.
   947  		const noSuchDirectory = "no such directory"
   948  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), noSuchDirectory) {
   949  			errstr := stderr.String()
   950  			abspath := strings.TrimSpace(errstr[strings.Index(errstr, noSuchDirectory)+len(noSuchDirectory):])
   951  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   952  				abspath, strings.Trim(stderr.String(), "\n"))
   953  			return bytes.NewBufferString(output), nil
   954  		}
   955  
   956  		// Workaround for #29280: go list -e has incorrect behavior when an ad-hoc package doesn't exist.
   957  		// Note that the error message we look for in this case is different that the one looked for above.
   958  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no such file or directory") {
   959  			output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   960  				strings.Trim(stderr.String(), "\n"))
   961  			return bytes.NewBufferString(output), nil
   962  		}
   963  
   964  		// Workaround for #34273. go list -e with GO111MODULE=on has incorrect behavior when listing a
   965  		// directory outside any module.
   966  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside available modules") {
   967  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   968  				// TODO(matloob): command-line-arguments isn't correct here.
   969  				"command-line-arguments", strings.Trim(stderr.String(), "\n"))
   970  			return bytes.NewBufferString(output), nil
   971  		}
   972  
   973  		// Another variation of the previous error
   974  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside module root") {
   975  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   976  				// TODO(matloob): command-line-arguments isn't correct here.
   977  				"command-line-arguments", strings.Trim(stderr.String(), "\n"))
   978  			return bytes.NewBufferString(output), nil
   979  		}
   980  
   981  		// Workaround for an instance of golang.org/issue/26755: go list -e  will return a non-zero exit
   982  		// status if there's a dependency on a package that doesn't exist. But it should return
   983  		// a zero exit status and set an error on that package.
   984  		if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no Go files in") {
   985  			// Don't clobber stdout if `go list` actually returned something.
   986  			if len(stdout.String()) > 0 {
   987  				return stdout, nil
   988  			}
   989  			// try to extract package name from string
   990  			stderrStr := stderr.String()
   991  			var importPath string
   992  			colon := strings.Index(stderrStr, ":")
   993  			if colon > 0 && strings.HasPrefix(stderrStr, "go build ") {
   994  				importPath = stderrStr[len("go build "):colon]
   995  			}
   996  			output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
   997  				importPath, strings.Trim(stderrStr, "\n"))
   998  			return bytes.NewBufferString(output), nil
   999  		}
  1000  
  1001  		// Export mode entails a build.
  1002  		// If that build fails, errors appear on stderr
  1003  		// (despite the -e flag) and the Export field is blank.
  1004  		// Do not fail in that case.
  1005  		// The same is true if an ad-hoc package given to go list doesn't exist.
  1006  		// TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when
  1007  		// packages don't exist or a build fails.
  1008  		if !usesExportData(cfg) && !containsGoFile(args) {
  1009  			return nil, friendlyErr
  1010  		}
  1011  	}
  1012  	return stdout, nil
  1013  }
  1014  
  1015  // OverlayJSON is the format overlay files are expected to be in.
  1016  // The Replace map maps from overlaid paths to replacement paths:
  1017  // the Go command will forward all reads trying to open
  1018  // each overlaid path to its replacement path, or consider the overlaid
  1019  // path not to exist if the replacement path is empty.
  1020  //
  1021  // From golang/go#39958.
  1022  type OverlayJSON struct {
  1023  	Replace map[string]string `json:"replace,omitempty"`
  1024  }
  1025  
  1026  // writeOverlays writes out files for go list's -overlay flag, as described
  1027  // above.
  1028  func (state *golistState) writeOverlays() (filename string, cleanup func(), err error) {
  1029  	// Do nothing if there are no overlays in the config.
  1030  	if len(state.cfg.Overlay) == 0 {
  1031  		return "", func() {}, nil
  1032  	}
  1033  	dir, err := ioutil.TempDir("", "gopackages-*")
  1034  	if err != nil {
  1035  		return "", nil, err
  1036  	}
  1037  	// The caller must clean up this directory, unless this function returns an
  1038  	// error.
  1039  	cleanup = func() {
  1040  		os.RemoveAll(dir)
  1041  	}
  1042  	defer func() {
  1043  		if err != nil {
  1044  			cleanup()
  1045  		}
  1046  	}()
  1047  	overlays := map[string]string{}
  1048  	for k, v := range state.cfg.Overlay {
  1049  		// Create a unique filename for the overlaid files, to avoid
  1050  		// creating nested directories.
  1051  		noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
  1052  		f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator))
  1053  		if err != nil {
  1054  			return "", func() {}, err
  1055  		}
  1056  		if _, err := f.Write(v); err != nil {
  1057  			return "", func() {}, err
  1058  		}
  1059  		if err := f.Close(); err != nil {
  1060  			return "", func() {}, err
  1061  		}
  1062  		overlays[k] = f.Name()
  1063  	}
  1064  	b, err := json.Marshal(OverlayJSON{Replace: overlays})
  1065  	if err != nil {
  1066  		return "", func() {}, err
  1067  	}
  1068  	// Write out the overlay file that contains the filepath mappings.
  1069  	filename = filepath.Join(dir, "overlay.json")
  1070  	if err := ioutil.WriteFile(filename, b, 0665); err != nil {
  1071  		return "", func() {}, err
  1072  	}
  1073  	return filename, cleanup, nil
  1074  }
  1075  
  1076  func containsGoFile(s []string) bool {
  1077  	for _, f := range s {
  1078  		if strings.HasSuffix(f, ".go") {
  1079  			return true
  1080  		}
  1081  	}
  1082  	return false
  1083  }
  1084  
  1085  func cmdDebugStr(cmd *exec.Cmd) string {
  1086  	env := make(map[string]string)
  1087  	for _, kv := range cmd.Env {
  1088  		split := strings.SplitN(kv, "=", 2)
  1089  		k, v := split[0], split[1]
  1090  		env[k] = v
  1091  	}
  1092  
  1093  	var args []string
  1094  	for _, arg := range cmd.Args {
  1095  		quoted := strconv.Quote(arg)
  1096  		if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") {
  1097  			args = append(args, quoted)
  1098  		} else {
  1099  			args = append(args, arg)
  1100  		}
  1101  	}
  1102  	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, " "))
  1103  }