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