Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / golang.org / x / tools / go / packages / golist.go
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         "encoding/json"
10         "fmt"
11         "go/types"
12         "io/ioutil"
13         "log"
14         "os"
15         "os/exec"
16         "path/filepath"
17         "reflect"
18         "regexp"
19         "strconv"
20         "strings"
21         "sync"
22         "time"
23
24         "golang.org/x/tools/go/internal/packagesdriver"
25         "golang.org/x/tools/internal/gopathwalk"
26         "golang.org/x/tools/internal/semver"
27 )
28
29 // debug controls verbose logging.
30 var 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.
34 type goTooOldError struct {
35         error
36 }
37
38 // responseDeduper wraps a driverResponse, deduplicating its contents.
39 type responseDeduper struct {
40         seenRoots    map[string]bool
41         seenPackages map[string]*Package
42         dr           *driverResponse
43 }
44
45 // init fills in r with a driverResponse.
46 func (r *responseDeduper) init(dr *driverResponse) {
47         r.dr = dr
48         r.seenRoots = map[string]bool{}
49         r.seenPackages = map[string]*Package{}
50         for _, pkg := range dr.Packages {
51                 r.seenPackages[pkg.ID] = pkg
52         }
53         for _, root := range dr.Roots {
54                 r.seenRoots[root] = true
55         }
56 }
57
58 func (r *responseDeduper) addPackage(p *Package) {
59         if r.seenPackages[p.ID] != nil {
60                 return
61         }
62         r.seenPackages[p.ID] = p
63         r.dr.Packages = append(r.dr.Packages, p)
64 }
65
66 func (r *responseDeduper) addRoot(id string) {
67         if r.seenRoots[id] {
68                 return
69         }
70         r.seenRoots[id] = true
71         r.dr.Roots = append(r.dr.Roots, id)
72 }
73
74 // goListDriver uses the go list command to interpret the patterns and produce
75 // the build system package structure.
76 // See driver for more details.
77 func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
78         var sizes types.Sizes
79         var sizeserr error
80         var sizeswg sync.WaitGroup
81         if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
82                 sizeswg.Add(1)
83                 go func() {
84                         sizes, sizeserr = getSizes(cfg)
85                         sizeswg.Done()
86                 }()
87         }
88
89         // Determine files requested in contains patterns
90         var containFiles []string
91         var packagesNamed []string
92         restPatterns := make([]string, 0, len(patterns))
93         // Extract file= and other [querytype]= patterns. Report an error if querytype
94         // doesn't exist.
95 extractQueries:
96         for _, pattern := range patterns {
97                 eqidx := strings.Index(pattern, "=")
98                 if eqidx < 0 {
99                         restPatterns = append(restPatterns, pattern)
100                 } else {
101                         query, value := pattern[:eqidx], pattern[eqidx+len("="):]
102                         switch query {
103                         case "file":
104                                 containFiles = append(containFiles, value)
105                         case "pattern":
106                                 restPatterns = append(restPatterns, value)
107                         case "iamashamedtousethedisabledqueryname":
108                                 packagesNamed = append(packagesNamed, value)
109                         case "": // not a reserved query
110                                 restPatterns = append(restPatterns, pattern)
111                         default:
112                                 for _, rune := range query {
113                                         if rune < 'a' || rune > 'z' { // not a reserved query
114                                                 restPatterns = append(restPatterns, pattern)
115                                                 continue extractQueries
116                                         }
117                                 }
118                                 // Reject all other patterns containing "="
119                                 return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
120                         }
121                 }
122         }
123
124         response := &responseDeduper{}
125         var err error
126
127         // See if we have any patterns to pass through to go list. Zero initial
128         // patterns also requires a go list call, since it's the equivalent of
129         // ".".
130         if len(restPatterns) > 0 || len(patterns) == 0 {
131                 dr, err := golistDriver(cfg, restPatterns...)
132                 if err != nil {
133                         return nil, err
134                 }
135                 response.init(dr)
136         } else {
137                 response.init(&driverResponse{})
138         }
139
140         sizeswg.Wait()
141         if sizeserr != nil {
142                 return nil, sizeserr
143         }
144         // types.SizesFor always returns nil or a *types.StdSizes
145         response.dr.Sizes, _ = sizes.(*types.StdSizes)
146
147         var containsCandidates []string
148
149         if len(containFiles) != 0 {
150                 if err := runContainsQueries(cfg, golistDriver, response, containFiles); err != nil {
151                         return nil, err
152                 }
153         }
154
155         if len(packagesNamed) != 0 {
156                 if err := runNamedQueries(cfg, golistDriver, response, packagesNamed); err != nil {
157                         return nil, err
158                 }
159         }
160
161         modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response.dr)
162         if err != nil {
163                 return nil, err
164         }
165         if len(containFiles) > 0 {
166                 containsCandidates = append(containsCandidates, modifiedPkgs...)
167                 containsCandidates = append(containsCandidates, needPkgs...)
168         }
169
170         if len(needPkgs) > 0 {
171                 addNeededOverlayPackages(cfg, golistDriver, response, needPkgs)
172                 if err != nil {
173                         return nil, err
174                 }
175         }
176         // Check candidate packages for containFiles.
177         if len(containFiles) > 0 {
178                 for _, id := range containsCandidates {
179                         pkg := response.seenPackages[id]
180                         for _, f := range containFiles {
181                                 for _, g := range pkg.GoFiles {
182                                         if sameFile(f, g) {
183                                                 response.addRoot(id)
184                                         }
185                                 }
186                         }
187                 }
188         }
189
190         return response.dr, nil
191 }
192
193 func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string) error {
194         dr, err := driver(cfg, pkgs...)
195         if err != nil {
196                 return err
197         }
198         for _, pkg := range dr.Packages {
199                 response.addPackage(pkg)
200         }
201         return nil
202 }
203
204 func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error {
205         for _, query := range queries {
206                 // TODO(matloob): Do only one query per directory.
207                 fdir := filepath.Dir(query)
208                 // Pass absolute path of directory to go list so that it knows to treat it as a directory,
209                 // not a package path.
210                 pattern, err := filepath.Abs(fdir)
211                 if err != nil {
212                         return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err)
213                 }
214                 dirResponse, err := driver(cfg, pattern)
215                 if err != nil {
216                         return err
217                 }
218                 isRoot := make(map[string]bool, len(dirResponse.Roots))
219                 for _, root := range dirResponse.Roots {
220                         isRoot[root] = true
221                 }
222                 for _, pkg := range dirResponse.Packages {
223                         // Add any new packages to the main set
224                         // We don't bother to filter packages that will be dropped by the changes of roots,
225                         // that will happen anyway during graph construction outside this function.
226                         // Over-reporting packages is not a problem.
227                         response.addPackage(pkg)
228                         // if the package was not a root one, it cannot have the file
229                         if !isRoot[pkg.ID] {
230                                 continue
231                         }
232                         for _, pkgFile := range pkg.GoFiles {
233                                 if filepath.Base(query) == filepath.Base(pkgFile) {
234                                         response.addRoot(pkg.ID)
235                                         break
236                                 }
237                         }
238                 }
239         }
240         return nil
241 }
242
243 // modCacheRegexp splits a path in a module cache into module, module version, and package.
244 var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
245
246 func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error {
247         // calling `go env` isn't free; bail out if there's nothing to do.
248         if len(queries) == 0 {
249                 return nil
250         }
251         // Determine which directories are relevant to scan.
252         roots, modRoot, err := roots(cfg)
253         if err != nil {
254                 return err
255         }
256
257         // Scan the selected directories. Simple matches, from GOPATH/GOROOT
258         // or the local module, can simply be "go list"ed. Matches from the
259         // module cache need special treatment.
260         var matchesMu sync.Mutex
261         var simpleMatches, modCacheMatches []string
262         add := func(root gopathwalk.Root, dir string) {
263                 // Walk calls this concurrently; protect the result slices.
264                 matchesMu.Lock()
265                 defer matchesMu.Unlock()
266
267                 path := dir
268                 if dir != root.Path {
269                         path = dir[len(root.Path)+1:]
270                 }
271                 if pathMatchesQueries(path, queries) {
272                         switch root.Type {
273                         case gopathwalk.RootModuleCache:
274                                 modCacheMatches = append(modCacheMatches, path)
275                         case gopathwalk.RootCurrentModule:
276                                 // We'd need to read go.mod to find the full
277                                 // import path. Relative's easier.
278                                 rel, err := filepath.Rel(cfg.Dir, dir)
279                                 if err != nil {
280                                         // This ought to be impossible, since
281                                         // we found dir in the current module.
282                                         panic(err)
283                                 }
284                                 simpleMatches = append(simpleMatches, "./"+rel)
285                         case gopathwalk.RootGOPATH, gopathwalk.RootGOROOT:
286                                 simpleMatches = append(simpleMatches, path)
287                         }
288                 }
289         }
290
291         startWalk := time.Now()
292         gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modRoot != "", Debug: debug})
293         if debug {
294                 log.Printf("%v for walk", time.Since(startWalk))
295         }
296
297         // Weird special case: the top-level package in a module will be in
298         // whatever directory the user checked the repository out into. It's
299         // more reasonable for that to not match the package name. So, if there
300         // are any Go files in the mod root, query it just to be safe.
301         if modRoot != "" {
302                 rel, err := filepath.Rel(cfg.Dir, modRoot)
303                 if err != nil {
304                         panic(err) // See above.
305                 }
306
307                 files, err := ioutil.ReadDir(modRoot)
308                 for _, f := range files {
309                         if strings.HasSuffix(f.Name(), ".go") {
310                                 simpleMatches = append(simpleMatches, rel)
311                                 break
312                         }
313                 }
314         }
315
316         addResponse := func(r *driverResponse) {
317                 for _, pkg := range r.Packages {
318                         response.addPackage(pkg)
319                         for _, name := range queries {
320                                 if pkg.Name == name {
321                                         response.addRoot(pkg.ID)
322                                         break
323                                 }
324                         }
325                 }
326         }
327
328         if len(simpleMatches) != 0 {
329                 resp, err := driver(cfg, simpleMatches...)
330                 if err != nil {
331                         return err
332                 }
333                 addResponse(resp)
334         }
335
336         // Module cache matches are tricky. We want to avoid downloading new
337         // versions of things, so we need to use the ones present in the cache.
338         // go list doesn't accept version specifiers, so we have to write out a
339         // temporary module, and do the list in that module.
340         if len(modCacheMatches) != 0 {
341                 // Collect all the matches, deduplicating by major version
342                 // and preferring the newest.
343                 type modInfo struct {
344                         mod   string
345                         major string
346                 }
347                 mods := make(map[modInfo]string)
348                 var imports []string
349                 for _, modPath := range modCacheMatches {
350                         matches := modCacheRegexp.FindStringSubmatch(modPath)
351                         mod, ver := filepath.ToSlash(matches[1]), matches[2]
352                         importPath := filepath.ToSlash(filepath.Join(matches[1], matches[3]))
353
354                         major := semver.Major(ver)
355                         if prevVer, ok := mods[modInfo{mod, major}]; !ok || semver.Compare(ver, prevVer) > 0 {
356                                 mods[modInfo{mod, major}] = ver
357                         }
358
359                         imports = append(imports, importPath)
360                 }
361
362                 // Build the temporary module.
363                 var gomod bytes.Buffer
364                 gomod.WriteString("module modquery\nrequire (\n")
365                 for mod, version := range mods {
366                         gomod.WriteString("\t" + mod.mod + " " + version + "\n")
367                 }
368                 gomod.WriteString(")\n")
369
370                 tmpCfg := *cfg
371
372                 // We're only trying to look at stuff in the module cache, so
373                 // disable the network. This should speed things up, and has
374                 // prevented errors in at least one case, #28518.
375                 tmpCfg.Env = append(append([]string{"GOPROXY=off"}, cfg.Env...))
376
377                 var err error
378                 tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery")
379                 if err != nil {
380                         return err
381                 }
382                 defer os.RemoveAll(tmpCfg.Dir)
383
384                 if err := ioutil.WriteFile(filepath.Join(tmpCfg.Dir, "go.mod"), gomod.Bytes(), 0777); err != nil {
385                         return fmt.Errorf("writing go.mod for module cache query: %v", err)
386                 }
387
388                 // Run the query, using the import paths calculated from the matches above.
389                 resp, err := driver(&tmpCfg, imports...)
390                 if err != nil {
391                         return fmt.Errorf("querying module cache matches: %v", err)
392                 }
393                 addResponse(resp)
394         }
395
396         return nil
397 }
398
399 func getSizes(cfg *Config) (types.Sizes, error) {
400         return packagesdriver.GetSizesGolist(cfg.Context, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg))
401 }
402
403 // roots selects the appropriate paths to walk based on the passed-in configuration,
404 // particularly the environment and the presence of a go.mod in cfg.Dir's parents.
405 func roots(cfg *Config) ([]gopathwalk.Root, string, error) {
406         stdout, err := invokeGo(cfg, "env", "GOROOT", "GOPATH", "GOMOD")
407         if err != nil {
408                 return nil, "", err
409         }
410
411         fields := strings.Split(stdout.String(), "\n")
412         if len(fields) != 4 || len(fields[3]) != 0 {
413                 return nil, "", fmt.Errorf("go env returned unexpected output: %q", stdout.String())
414         }
415         goroot, gopath, gomod := fields[0], filepath.SplitList(fields[1]), fields[2]
416         var modDir string
417         if gomod != "" {
418                 modDir = filepath.Dir(gomod)
419         }
420
421         var roots []gopathwalk.Root
422         // Always add GOROOT.
423         roots = append(roots, gopathwalk.Root{filepath.Join(goroot, "/src"), gopathwalk.RootGOROOT})
424         // If modules are enabled, scan the module dir.
425         if modDir != "" {
426                 roots = append(roots, gopathwalk.Root{modDir, gopathwalk.RootCurrentModule})
427         }
428         // Add either GOPATH/src or GOPATH/pkg/mod, depending on module mode.
429         for _, p := range gopath {
430                 if modDir != "" {
431                         roots = append(roots, gopathwalk.Root{filepath.Join(p, "/pkg/mod"), gopathwalk.RootModuleCache})
432                 } else {
433                         roots = append(roots, gopathwalk.Root{filepath.Join(p, "/src"), gopathwalk.RootGOPATH})
434                 }
435         }
436
437         return roots, modDir, nil
438 }
439
440 // These functions were copied from goimports. See further documentation there.
441
442 // pathMatchesQueries is adapted from pkgIsCandidate.
443 // TODO: is it reasonable to do Contains here, rather than an exact match on a path component?
444 func pathMatchesQueries(path string, queries []string) bool {
445         lastTwo := lastTwoComponents(path)
446         for _, query := range queries {
447                 if strings.Contains(lastTwo, query) {
448                         return true
449                 }
450                 if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(query) {
451                         lastTwo = lowerASCIIAndRemoveHyphen(lastTwo)
452                         if strings.Contains(lastTwo, query) {
453                                 return true
454                         }
455                 }
456         }
457         return false
458 }
459
460 // lastTwoComponents returns at most the last two path components
461 // of v, using either / or \ as the path separator.
462 func lastTwoComponents(v string) string {
463         nslash := 0
464         for i := len(v) - 1; i >= 0; i-- {
465                 if v[i] == '/' || v[i] == '\\' {
466                         nslash++
467                         if nslash == 2 {
468                                 return v[i:]
469                         }
470                 }
471         }
472         return v
473 }
474
475 func hasHyphenOrUpperASCII(s string) bool {
476         for i := 0; i < len(s); i++ {
477                 b := s[i]
478                 if b == '-' || ('A' <= b && b <= 'Z') {
479                         return true
480                 }
481         }
482         return false
483 }
484
485 func lowerASCIIAndRemoveHyphen(s string) (ret string) {
486         buf := make([]byte, 0, len(s))
487         for i := 0; i < len(s); i++ {
488                 b := s[i]
489                 switch {
490                 case b == '-':
491                         continue
492                 case 'A' <= b && b <= 'Z':
493                         buf = append(buf, b+('a'-'A'))
494                 default:
495                         buf = append(buf, b)
496                 }
497         }
498         return string(buf)
499 }
500
501 // Fields must match go list;
502 // see $GOROOT/src/cmd/go/internal/load/pkg.go.
503 type jsonPackage struct {
504         ImportPath      string
505         Dir             string
506         Name            string
507         Export          string
508         GoFiles         []string
509         CompiledGoFiles []string
510         CFiles          []string
511         CgoFiles        []string
512         CXXFiles        []string
513         MFiles          []string
514         HFiles          []string
515         FFiles          []string
516         SFiles          []string
517         SwigFiles       []string
518         SwigCXXFiles    []string
519         SysoFiles       []string
520         Imports         []string
521         ImportMap       map[string]string
522         Deps            []string
523         TestGoFiles     []string
524         TestImports     []string
525         XTestGoFiles    []string
526         XTestImports    []string
527         ForTest         string // q in a "p [q.test]" package, else ""
528         DepOnly         bool
529
530         Error *jsonPackageError
531 }
532
533 type jsonPackageError struct {
534         ImportStack []string
535         Pos         string
536         Err         string
537 }
538
539 func otherFiles(p *jsonPackage) [][]string {
540         return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
541 }
542
543 // golistDriver uses the "go list" command to expand the pattern
544 // words and return metadata for the specified packages. dir may be
545 // "" and env may be nil, as per os/exec.Command.
546 func golistDriver(cfg *Config, words ...string) (*driverResponse, error) {
547         // go list uses the following identifiers in ImportPath and Imports:
548         //
549         //      "p"                     -- importable package or main (command)
550         //      "q.test"                -- q's test executable
551         //      "p [q.test]"            -- variant of p as built for q's test executable
552         //      "q_test [q.test]"       -- q's external test package
553         //
554         // The packages p that are built differently for a test q.test
555         // are q itself, plus any helpers used by the external test q_test,
556         // typically including "testing" and all its dependencies.
557
558         // Run "go list" for complete
559         // information on the specified packages.
560         buf, err := invokeGo(cfg, golistargs(cfg, words)...)
561         if err != nil {
562                 return nil, err
563         }
564         seen := make(map[string]*jsonPackage)
565         // Decode the JSON and convert it to Package form.
566         var response driverResponse
567         for dec := json.NewDecoder(buf); dec.More(); {
568                 p := new(jsonPackage)
569                 if err := dec.Decode(p); err != nil {
570                         return nil, fmt.Errorf("JSON decoding failed: %v", err)
571                 }
572
573                 if p.ImportPath == "" {
574                         // The documentation for go list says that “[e]rroneous packages will have
575                         // a non-empty ImportPath”. If for some reason it comes back empty, we
576                         // prefer to error out rather than silently discarding data or handing
577                         // back a package without any way to refer to it.
578                         if p.Error != nil {
579                                 return nil, Error{
580                                         Pos: p.Error.Pos,
581                                         Msg: p.Error.Err,
582                                 }
583                         }
584                         return nil, fmt.Errorf("package missing import path: %+v", p)
585                 }
586
587                 if old, found := seen[p.ImportPath]; found {
588                         if !reflect.DeepEqual(p, old) {
589                                 return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
590                         }
591                         // skip the duplicate
592                         continue
593                 }
594                 seen[p.ImportPath] = p
595
596                 pkg := &Package{
597                         Name:            p.Name,
598                         ID:              p.ImportPath,
599                         GoFiles:         absJoin(p.Dir, p.GoFiles, p.CgoFiles),
600                         CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
601                         OtherFiles:      absJoin(p.Dir, otherFiles(p)...),
602                 }
603
604                 // Work around https://golang.org/issue/28749:
605                 // cmd/go puts assembly, C, and C++ files in CompiledGoFiles.
606                 // Filter out any elements of CompiledGoFiles that are also in OtherFiles.
607                 // We have to keep this workaround in place until go1.12 is a distant memory.
608                 if len(pkg.OtherFiles) > 0 {
609                         other := make(map[string]bool, len(pkg.OtherFiles))
610                         for _, f := range pkg.OtherFiles {
611                                 other[f] = true
612                         }
613
614                         out := pkg.CompiledGoFiles[:0]
615                         for _, f := range pkg.CompiledGoFiles {
616                                 if other[f] {
617                                         continue
618                                 }
619                                 out = append(out, f)
620                         }
621                         pkg.CompiledGoFiles = out
622                 }
623
624                 // Extract the PkgPath from the package's ID.
625                 if i := strings.IndexByte(pkg.ID, ' '); i >= 0 {
626                         pkg.PkgPath = pkg.ID[:i]
627                 } else {
628                         pkg.PkgPath = pkg.ID
629                 }
630
631                 if pkg.PkgPath == "unsafe" {
632                         pkg.GoFiles = nil // ignore fake unsafe.go file
633                 }
634
635                 // Assume go list emits only absolute paths for Dir.
636                 if p.Dir != "" && !filepath.IsAbs(p.Dir) {
637                         log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
638                 }
639
640                 if p.Export != "" && !filepath.IsAbs(p.Export) {
641                         pkg.ExportFile = filepath.Join(p.Dir, p.Export)
642                 } else {
643                         pkg.ExportFile = p.Export
644                 }
645
646                 // imports
647                 //
648                 // Imports contains the IDs of all imported packages.
649                 // ImportsMap records (path, ID) only where they differ.
650                 ids := make(map[string]bool)
651                 for _, id := range p.Imports {
652                         ids[id] = true
653                 }
654                 pkg.Imports = make(map[string]*Package)
655                 for path, id := range p.ImportMap {
656                         pkg.Imports[path] = &Package{ID: id} // non-identity import
657                         delete(ids, id)
658                 }
659                 for id := range ids {
660                         if id == "C" {
661                                 continue
662                         }
663
664                         pkg.Imports[id] = &Package{ID: id} // identity import
665                 }
666                 if !p.DepOnly {
667                         response.Roots = append(response.Roots, pkg.ID)
668                 }
669
670                 // Work around for pre-go.1.11 versions of go list.
671                 // TODO(matloob): they should be handled by the fallback.
672                 // Can we delete this?
673                 if len(pkg.CompiledGoFiles) == 0 {
674                         pkg.CompiledGoFiles = pkg.GoFiles
675                 }
676
677                 if p.Error != nil {
678                         pkg.Errors = append(pkg.Errors, Error{
679                                 Pos: p.Error.Pos,
680                                 Msg: p.Error.Err,
681                         })
682                 }
683
684                 response.Packages = append(response.Packages, pkg)
685         }
686
687         return &response, nil
688 }
689
690 // absJoin absolutizes and flattens the lists of files.
691 func absJoin(dir string, fileses ...[]string) (res []string) {
692         for _, files := range fileses {
693                 for _, file := range files {
694                         if !filepath.IsAbs(file) {
695                                 file = filepath.Join(dir, file)
696                         }
697                         res = append(res, file)
698                 }
699         }
700         return res
701 }
702
703 func golistargs(cfg *Config, words []string) []string {
704         const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
705         fullargs := []string{
706                 "list", "-e", "-json",
707                 fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypesInfo|NeedTypesSizes) != 0),
708                 fmt.Sprintf("-test=%t", cfg.Tests),
709                 fmt.Sprintf("-export=%t", usesExportData(cfg)),
710                 fmt.Sprintf("-deps=%t", cfg.Mode&NeedDeps != 0),
711                 // go list doesn't let you pass -test and -find together,
712                 // probably because you'd just get the TestMain.
713                 fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0),
714         }
715         fullargs = append(fullargs, cfg.BuildFlags...)
716         fullargs = append(fullargs, "--")
717         fullargs = append(fullargs, words...)
718         return fullargs
719 }
720
721 // invokeGo returns the stdout of a go command invocation.
722 func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) {
723         stdout := new(bytes.Buffer)
724         stderr := new(bytes.Buffer)
725         cmd := exec.CommandContext(cfg.Context, "go", args...)
726         // On darwin the cwd gets resolved to the real path, which breaks anything that
727         // expects the working directory to keep the original path, including the
728         // go command when dealing with modules.
729         // The Go stdlib has a special feature where if the cwd and the PWD are the
730         // same node then it trusts the PWD, so by setting it in the env for the child
731         // process we fix up all the paths returned by the go command.
732         cmd.Env = append(append([]string{}, cfg.Env...), "PWD="+cfg.Dir)
733         cmd.Dir = cfg.Dir
734         cmd.Stdout = stdout
735         cmd.Stderr = stderr
736         if debug {
737                 defer func(start time.Time) {
738                         log.Printf("%s for %v, stderr: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, args...), stderr)
739                 }(time.Now())
740         }
741
742         if err := cmd.Run(); err != nil {
743                 // Check for 'go' executable not being found.
744                 if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
745                         return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
746                 }
747
748                 exitErr, ok := err.(*exec.ExitError)
749                 if !ok {
750                         // Catastrophic error:
751                         // - context cancellation
752                         return nil, fmt.Errorf("couldn't exec 'go %v': %s %T", args, err, err)
753                 }
754
755                 // Old go version?
756                 if strings.Contains(stderr.String(), "flag provided but not defined") {
757                         return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)}
758                 }
759
760                 // This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show
761                 // the error in the Err section of stdout in case -e option is provided.
762                 // This fix is provided for backwards compatibility.
763                 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must be .go files") {
764                         output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
765                                 strings.Trim(stderr.String(), "\n"))
766                         return bytes.NewBufferString(output), nil
767                 }
768
769                 // Workaround for #29280: go list -e has incorrect behavior when an ad-hoc package doesn't exist.
770                 if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no such file or directory") {
771                         output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
772                                 strings.Trim(stderr.String(), "\n"))
773                         return bytes.NewBufferString(output), nil
774                 }
775
776                 // Export mode entails a build.
777                 // If that build fails, errors appear on stderr
778                 // (despite the -e flag) and the Export field is blank.
779                 // Do not fail in that case.
780                 // The same is true if an ad-hoc package given to go list doesn't exist.
781                 // TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when
782                 // packages don't exist or a build fails.
783                 if !usesExportData(cfg) && !containsGoFile(args) {
784                         return nil, fmt.Errorf("go %v: %s: %s", args, exitErr, stderr)
785                 }
786         }
787
788         // As of writing, go list -export prints some non-fatal compilation
789         // errors to stderr, even with -e set. We would prefer that it put
790         // them in the Package.Error JSON (see https://golang.org/issue/26319).
791         // In the meantime, there's nowhere good to put them, but they can
792         // be useful for debugging. Print them if $GOPACKAGESPRINTGOLISTERRORS
793         // is set.
794         if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTGOLISTERRORS") != "" {
795                 fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd, args...), stderr)
796         }
797
798         // debugging
799         if false {
800                 fmt.Fprintf(os.Stderr, "%s stdout: <<%s>>\n", cmdDebugStr(cmd, args...), stdout)
801         }
802
803         return stdout, nil
804 }
805
806 func containsGoFile(s []string) bool {
807         for _, f := range s {
808                 if strings.HasSuffix(f, ".go") {
809                         return true
810                 }
811         }
812         return false
813 }
814
815 func cmdDebugStr(cmd *exec.Cmd, args ...string) string {
816         env := make(map[string]string)
817         for _, kv := range cmd.Env {
818                 split := strings.Split(kv, "=")
819                 k, v := split[0], split[1]
820                 env[k] = v
821         }
822         var quotedArgs []string
823         for _, arg := range args {
824                 quotedArgs = append(quotedArgs, strconv.Quote(arg))
825         }
826
827         return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v PWD=%v go %s", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["PWD"], strings.Join(quotedArgs, " "))
828 }