You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

996 lines
33 KiB

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