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