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