github.com/jgarto/itcv@v0.0.0-20180826224514-4eea09c1aa0d/_vendor/src/golang.org/x/tools/imports/fix.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package imports 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "go/ast" 12 "go/build" 13 "go/parser" 14 "go/token" 15 "io/ioutil" 16 "log" 17 "os" 18 "path" 19 "path/filepath" 20 "sort" 21 "strings" 22 "sync" 23 24 "golang.org/x/tools/go/ast/astutil" 25 "golang.org/x/tools/internal/fastwalk" 26 ) 27 28 // Debug controls verbose logging. 29 var Debug = false 30 31 var ( 32 inTests = false // set true by fix_test.go; if false, no need to use testMu 33 testMu sync.RWMutex // guards globals reset by tests; used only if inTests 34 ) 35 36 // LocalPrefix is a comma-separated string of import path prefixes, which, if 37 // set, instructs Process to sort the import paths with the given prefixes 38 // into another group after 3rd-party packages. 39 var LocalPrefix string 40 41 func localPrefixes() []string { 42 if LocalPrefix != "" { 43 return strings.Split(LocalPrefix, ",") 44 } 45 return nil 46 } 47 48 // importToGroup is a list of functions which map from an import path to 49 // a group number. 50 var importToGroup = []func(importPath string) (num int, ok bool){ 51 func(importPath string) (num int, ok bool) { 52 for _, p := range localPrefixes() { 53 if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath { 54 return 3, true 55 } 56 } 57 return 58 }, 59 func(importPath string) (num int, ok bool) { 60 if strings.HasPrefix(importPath, "appengine") { 61 return 2, true 62 } 63 return 64 }, 65 func(importPath string) (num int, ok bool) { 66 if strings.Contains(importPath, ".") { 67 return 1, true 68 } 69 return 70 }, 71 } 72 73 func importGroup(importPath string) int { 74 for _, fn := range importToGroup { 75 if n, ok := fn(importPath); ok { 76 return n 77 } 78 } 79 return 0 80 } 81 82 // importInfo is a summary of information about one import. 83 type importInfo struct { 84 Path string // full import path (e.g. "crypto/rand") 85 Alias string // import alias, if present (e.g. "crand") 86 } 87 88 // packageInfo is a summary of features found in a package. 89 type packageInfo struct { 90 Globals map[string]bool // symbol => true 91 Imports map[string]importInfo // pkg base name or alias => info 92 // refs are a set of package references currently satisfied by imports. 93 // first key: either base package (e.g. "fmt") or renamed package 94 // second key: referenced package symbol (e.g. "Println") 95 Refs map[string]map[string]bool 96 } 97 98 // dirPackageInfo exposes the dirPackageInfoFile function so that it can be overridden. 99 var dirPackageInfo = dirPackageInfoFile 100 101 // dirPackageInfoFile gets information from other files in the package. 102 func dirPackageInfoFile(pkgName, srcDir, filename string) (*packageInfo, error) { 103 considerTests := strings.HasSuffix(filename, "_test.go") 104 105 fileBase := filepath.Base(filename) 106 packageFileInfos, err := ioutil.ReadDir(srcDir) 107 if err != nil { 108 return nil, err 109 } 110 111 info := &packageInfo{ 112 Globals: make(map[string]bool), 113 Imports: make(map[string]importInfo), 114 Refs: make(map[string]map[string]bool), 115 } 116 117 visitor := collectReferences(info.Refs) 118 for _, fi := range packageFileInfos { 119 if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") { 120 continue 121 } 122 if !considerTests && strings.HasSuffix(fi.Name(), "_test.go") { 123 continue 124 } 125 126 fileSet := token.NewFileSet() 127 root, err := parser.ParseFile(fileSet, filepath.Join(srcDir, fi.Name()), nil, 0) 128 if err != nil { 129 continue 130 } 131 132 for _, decl := range root.Decls { 133 genDecl, ok := decl.(*ast.GenDecl) 134 if !ok { 135 continue 136 } 137 138 for _, spec := range genDecl.Specs { 139 valueSpec, ok := spec.(*ast.ValueSpec) 140 if !ok { 141 continue 142 } 143 info.Globals[valueSpec.Names[0].Name] = true 144 } 145 } 146 147 for _, imp := range root.Imports { 148 impInfo := importInfo{Path: strings.Trim(imp.Path.Value, `"`)} 149 name := path.Base(impInfo.Path) 150 if imp.Name != nil { 151 name = strings.Trim(imp.Name.Name, `"`) 152 impInfo.Alias = name 153 } 154 info.Imports[name] = impInfo 155 } 156 157 ast.Walk(visitor, root) 158 } 159 return info, nil 160 } 161 162 // collectReferences returns a visitor that collects all exported package 163 // references 164 func collectReferences(refs map[string]map[string]bool) visitFn { 165 var visitor visitFn 166 visitor = func(node ast.Node) ast.Visitor { 167 if node == nil { 168 return visitor 169 } 170 switch v := node.(type) { 171 case *ast.SelectorExpr: 172 xident, ok := v.X.(*ast.Ident) 173 if !ok { 174 break 175 } 176 if xident.Obj != nil { 177 // if the parser can resolve it, it's not a package ref 178 break 179 } 180 pkgName := xident.Name 181 r := refs[pkgName] 182 if r == nil { 183 r = make(map[string]bool) 184 refs[pkgName] = r 185 } 186 if ast.IsExported(v.Sel.Name) { 187 r[v.Sel.Name] = true 188 } 189 } 190 return visitor 191 } 192 return visitor 193 } 194 195 func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []string, err error) { 196 // refs are a set of possible package references currently unsatisfied by imports. 197 // first key: either base package (e.g. "fmt") or renamed package 198 // second key: referenced package symbol (e.g. "Println") 199 refs := make(map[string]map[string]bool) 200 201 // decls are the current package imports. key is base package or renamed package. 202 decls := make(map[string]*ast.ImportSpec) 203 204 abs, err := filepath.Abs(filename) 205 if err != nil { 206 return nil, err 207 } 208 srcDir := filepath.Dir(abs) 209 if Debug { 210 log.Printf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir) 211 } 212 213 var packageInfo *packageInfo 214 var loadedPackageInfo bool 215 216 // collect potential uses of packages. 217 var visitor visitFn 218 visitor = visitFn(func(node ast.Node) ast.Visitor { 219 if node == nil { 220 return visitor 221 } 222 switch v := node.(type) { 223 case *ast.ImportSpec: 224 if v.Name != nil { 225 decls[v.Name.Name] = v 226 break 227 } 228 ipath := strings.Trim(v.Path.Value, `"`) 229 if ipath == "C" { 230 break 231 } 232 local := importPathToName(ipath, srcDir) 233 decls[local] = v 234 case *ast.SelectorExpr: 235 xident, ok := v.X.(*ast.Ident) 236 if !ok { 237 break 238 } 239 if xident.Obj != nil { 240 // if the parser can resolve it, it's not a package ref 241 break 242 } 243 pkgName := xident.Name 244 if refs[pkgName] == nil { 245 refs[pkgName] = make(map[string]bool) 246 } 247 if !loadedPackageInfo { 248 loadedPackageInfo = true 249 packageInfo, _ = dirPackageInfo(f.Name.Name, srcDir, filename) 250 } 251 if decls[pkgName] == nil && (packageInfo == nil || !packageInfo.Globals[pkgName]) { 252 refs[pkgName][v.Sel.Name] = true 253 } 254 } 255 return visitor 256 }) 257 ast.Walk(visitor, f) 258 259 // Nil out any unused ImportSpecs, to be removed in following passes 260 unusedImport := map[string]string{} 261 for pkg, is := range decls { 262 if refs[pkg] == nil && pkg != "_" && pkg != "." { 263 name := "" 264 if is.Name != nil { 265 name = is.Name.Name 266 } 267 unusedImport[strings.Trim(is.Path.Value, `"`)] = name 268 } 269 } 270 for ipath, name := range unusedImport { 271 if ipath == "C" { 272 // Don't remove cgo stuff. 273 continue 274 } 275 astutil.DeleteNamedImport(fset, f, name, ipath) 276 } 277 278 for pkgName, symbols := range refs { 279 if len(symbols) == 0 { 280 // skip over packages already imported 281 delete(refs, pkgName) 282 } 283 } 284 285 // Fast path, all references already imported. 286 if len(refs) == 0 { 287 return nil, nil 288 } 289 290 // Can assume this will be necessary in all cases now. 291 if !loadedPackageInfo { 292 packageInfo, _ = dirPackageInfo(f.Name.Name, srcDir, filename) 293 } 294 295 // Search for imports matching potential package references. 296 searches := 0 297 type result struct { 298 ipath string // import path (if err == nil) 299 name string // optional name to rename import as 300 err error 301 } 302 results := make(chan result) 303 for pkgName, symbols := range refs { 304 go func(pkgName string, symbols map[string]bool) { 305 if packageInfo != nil { 306 sibling := packageInfo.Imports[pkgName] 307 if sibling.Path != "" { 308 refs := packageInfo.Refs[pkgName] 309 for symbol := range symbols { 310 if refs[symbol] { 311 results <- result{ipath: sibling.Path, name: sibling.Alias} 312 return 313 } 314 } 315 } 316 } 317 ipath, rename, err := findImport(pkgName, symbols, filename) 318 r := result{ipath: ipath, err: err} 319 if rename { 320 r.name = pkgName 321 } 322 results <- r 323 }(pkgName, symbols) 324 searches++ 325 } 326 for i := 0; i < searches; i++ { 327 result := <-results 328 if result.err != nil { 329 return nil, result.err 330 } 331 if result.ipath != "" { 332 if result.name != "" { 333 astutil.AddNamedImport(fset, f, result.name, result.ipath) 334 } else { 335 astutil.AddImport(fset, f, result.ipath) 336 } 337 added = append(added, result.ipath) 338 } 339 } 340 341 return added, nil 342 } 343 344 // importPathToName returns the package name for the given import path. 345 var importPathToName func(importPath, srcDir string) (packageName string) = importPathToNameGoPath 346 347 // importPathToNameBasic assumes the package name is the base of import path. 348 func importPathToNameBasic(importPath, srcDir string) (packageName string) { 349 return path.Base(importPath) 350 } 351 352 // importPathToNameGoPath finds out the actual package name, as declared in its .go files. 353 // If there's a problem, it falls back to using importPathToNameBasic. 354 func importPathToNameGoPath(importPath, srcDir string) (packageName string) { 355 // Fast path for standard library without going to disk. 356 if pkg, ok := stdImportPackage[importPath]; ok { 357 return pkg 358 } 359 360 pkgName, err := importPathToNameGoPathParse(importPath, srcDir) 361 if Debug { 362 log.Printf("importPathToNameGoPathParse(%q, srcDir=%q) = %q, %v", importPath, srcDir, pkgName, err) 363 } 364 if err == nil { 365 return pkgName 366 } 367 return importPathToNameBasic(importPath, srcDir) 368 } 369 370 // importPathToNameGoPathParse is a faster version of build.Import if 371 // the only thing desired is the package name. It uses build.FindOnly 372 // to find the directory and then only parses one file in the package, 373 // trusting that the files in the directory are consistent. 374 func importPathToNameGoPathParse(importPath, srcDir string) (packageName string, err error) { 375 buildPkg, err := build.Import(importPath, srcDir, build.FindOnly) 376 if err != nil { 377 return "", err 378 } 379 d, err := os.Open(buildPkg.Dir) 380 if err != nil { 381 return "", err 382 } 383 names, err := d.Readdirnames(-1) 384 d.Close() 385 if err != nil { 386 return "", err 387 } 388 sort.Strings(names) // to have predictable behavior 389 var lastErr error 390 var nfile int 391 for _, name := range names { 392 if !strings.HasSuffix(name, ".go") { 393 continue 394 } 395 if strings.HasSuffix(name, "_test.go") { 396 continue 397 } 398 nfile++ 399 fullFile := filepath.Join(buildPkg.Dir, name) 400 401 fset := token.NewFileSet() 402 f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly) 403 if err != nil { 404 lastErr = err 405 continue 406 } 407 pkgName := f.Name.Name 408 if pkgName == "documentation" { 409 // Special case from go/build.ImportDir, not 410 // handled by ctx.MatchFile. 411 continue 412 } 413 if pkgName == "main" { 414 // Also skip package main, assuming it's a +build ignore generator or example. 415 // Since you can't import a package main anyway, there's no harm here. 416 continue 417 } 418 return pkgName, nil 419 } 420 if lastErr != nil { 421 return "", lastErr 422 } 423 return "", fmt.Errorf("no importable package found in %d Go files", nfile) 424 } 425 426 var stdImportPackage = map[string]string{} // "net/http" => "http" 427 428 func init() { 429 // Nothing in the standard library has a package name not 430 // matching its import base name. 431 for _, pkg := range stdlib { 432 if _, ok := stdImportPackage[pkg]; !ok { 433 stdImportPackage[pkg] = path.Base(pkg) 434 } 435 } 436 } 437 438 // Directory-scanning state. 439 var ( 440 // scanGoRootOnce guards calling scanGoRoot (for $GOROOT) 441 scanGoRootOnce sync.Once 442 // scanGoPathOnce guards calling scanGoPath (for $GOPATH) 443 scanGoPathOnce sync.Once 444 445 // populateIgnoreOnce guards calling populateIgnore 446 populateIgnoreOnce sync.Once 447 ignoredDirs []os.FileInfo 448 449 dirScanMu sync.RWMutex 450 dirScan map[string]*pkg // abs dir path => *pkg 451 ) 452 453 type pkg struct { 454 dir string // absolute file path to pkg directory ("/usr/lib/go/src/net/http") 455 importPath string // full pkg import path ("net/http", "foo/bar/vendor/a/b") 456 importPathShort string // vendorless import path ("net/http", "a/b") 457 distance int // relative distance to target 458 } 459 460 // byDistanceOrImportPathShortLength sorts by relative distance breaking ties 461 // on the short import path length and then the import string itself. 462 type byDistanceOrImportPathShortLength []*pkg 463 464 func (s byDistanceOrImportPathShortLength) Len() int { return len(s) } 465 func (s byDistanceOrImportPathShortLength) Less(i, j int) bool { 466 di, dj := s[i].distance, s[j].distance 467 if di == -1 { 468 return false 469 } 470 if dj == -1 { 471 return true 472 } 473 if di != dj { 474 return di < dj 475 } 476 477 vi, vj := s[i].importPathShort, s[j].importPathShort 478 if len(vi) != len(vj) { 479 return len(vi) < len(vj) 480 } 481 return vi < vj 482 } 483 func (s byDistanceOrImportPathShortLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 484 485 func distance(basepath, targetpath string) int { 486 p, err := filepath.Rel(basepath, targetpath) 487 if err != nil { 488 return -1 489 } 490 if p == "." { 491 return 0 492 } 493 return strings.Count(p, string(filepath.Separator)) + 1 494 } 495 496 // guarded by populateIgnoreOnce; populates ignoredDirs. 497 func populateIgnore() { 498 for _, srcDir := range build.Default.SrcDirs() { 499 if srcDir == filepath.Join(build.Default.GOROOT, "src") { 500 continue 501 } 502 populateIgnoredDirs(srcDir) 503 } 504 } 505 506 // populateIgnoredDirs reads an optional config file at <path>/.goimportsignore 507 // of relative directories to ignore when scanning for go files. 508 // The provided path is one of the $GOPATH entries with "src" appended. 509 func populateIgnoredDirs(path string) { 510 file := filepath.Join(path, ".goimportsignore") 511 slurp, err := ioutil.ReadFile(file) 512 if Debug { 513 if err != nil { 514 log.Print(err) 515 } else { 516 log.Printf("Read %s", file) 517 } 518 } 519 if err != nil { 520 return 521 } 522 bs := bufio.NewScanner(bytes.NewReader(slurp)) 523 for bs.Scan() { 524 line := strings.TrimSpace(bs.Text()) 525 if line == "" || strings.HasPrefix(line, "#") { 526 continue 527 } 528 full := filepath.Join(path, line) 529 if fi, err := os.Stat(full); err == nil { 530 ignoredDirs = append(ignoredDirs, fi) 531 if Debug { 532 log.Printf("Directory added to ignore list: %s", full) 533 } 534 } else if Debug { 535 log.Printf("Error statting entry in .goimportsignore: %v", err) 536 } 537 } 538 } 539 540 func skipDir(fi os.FileInfo) bool { 541 for _, ignoredDir := range ignoredDirs { 542 if os.SameFile(fi, ignoredDir) { 543 return true 544 } 545 } 546 return false 547 } 548 549 // shouldTraverse reports whether the symlink fi, found in dir, 550 // should be followed. It makes sure symlinks were never visited 551 // before to avoid symlink loops. 552 func shouldTraverse(dir string, fi os.FileInfo) bool { 553 path := filepath.Join(dir, fi.Name()) 554 target, err := filepath.EvalSymlinks(path) 555 if err != nil { 556 return false 557 } 558 ts, err := os.Stat(target) 559 if err != nil { 560 fmt.Fprintln(os.Stderr, err) 561 return false 562 } 563 if !ts.IsDir() { 564 return false 565 } 566 if skipDir(ts) { 567 return false 568 } 569 // Check for symlink loops by statting each directory component 570 // and seeing if any are the same file as ts. 571 for { 572 parent := filepath.Dir(path) 573 if parent == path { 574 // Made it to the root without seeing a cycle. 575 // Use this symlink. 576 return true 577 } 578 parentInfo, err := os.Stat(parent) 579 if err != nil { 580 return false 581 } 582 if os.SameFile(ts, parentInfo) { 583 // Cycle. Don't traverse. 584 return false 585 } 586 path = parent 587 } 588 589 } 590 591 var testHookScanDir = func(dir string) {} 592 593 var scanGoRootDone = make(chan struct{}) // closed when scanGoRoot is done 594 595 func scanGoRoot() { 596 go func() { 597 scanGoDirs(true) 598 close(scanGoRootDone) 599 }() 600 } 601 602 func scanGoPath() { scanGoDirs(false) } 603 604 func scanGoDirs(goRoot bool) { 605 if Debug { 606 which := "$GOROOT" 607 if !goRoot { 608 which = "$GOPATH" 609 } 610 log.Printf("scanning " + which) 611 defer log.Printf("scanned " + which) 612 } 613 dirScanMu.Lock() 614 if dirScan == nil { 615 dirScan = make(map[string]*pkg) 616 } 617 dirScanMu.Unlock() 618 619 for _, srcDir := range build.Default.SrcDirs() { 620 isGoroot := srcDir == filepath.Join(build.Default.GOROOT, "src") 621 if isGoroot != goRoot { 622 continue 623 } 624 testHookScanDir(srcDir) 625 walkFn := func(path string, typ os.FileMode) error { 626 dir := filepath.Dir(path) 627 if typ.IsRegular() { 628 if dir == srcDir { 629 // Doesn't make sense to have regular files 630 // directly in your $GOPATH/src or $GOROOT/src. 631 return nil 632 } 633 if !strings.HasSuffix(path, ".go") { 634 return nil 635 } 636 dirScanMu.Lock() 637 if _, dup := dirScan[dir]; !dup { 638 importpath := filepath.ToSlash(dir[len(srcDir)+len("/"):]) 639 dirScan[dir] = &pkg{ 640 importPath: importpath, 641 importPathShort: vendorlessImportPath(importpath), 642 dir: dir, 643 } 644 } 645 dirScanMu.Unlock() 646 return nil 647 } 648 if typ == os.ModeDir { 649 base := filepath.Base(path) 650 if base == "" || base[0] == '.' || base[0] == '_' || 651 base == "testdata" || base == "node_modules" { 652 return filepath.SkipDir 653 } 654 fi, err := os.Lstat(path) 655 if err == nil && skipDir(fi) { 656 if Debug { 657 log.Printf("skipping directory %q under %s", fi.Name(), dir) 658 } 659 return filepath.SkipDir 660 } 661 return nil 662 } 663 if typ == os.ModeSymlink { 664 base := filepath.Base(path) 665 if strings.HasPrefix(base, ".#") { 666 // Emacs noise. 667 return nil 668 } 669 fi, err := os.Lstat(path) 670 if err != nil { 671 // Just ignore it. 672 return nil 673 } 674 if shouldTraverse(dir, fi) { 675 return fastwalk.TraverseLink 676 } 677 } 678 return nil 679 } 680 if err := fastwalk.Walk(srcDir, walkFn); err != nil { 681 log.Printf("goimports: scanning directory %v: %v", srcDir, err) 682 } 683 } 684 } 685 686 // vendorlessImportPath returns the devendorized version of the provided import path. 687 // e.g. "foo/bar/vendor/a/b" => "a/b" 688 func vendorlessImportPath(ipath string) string { 689 // Devendorize for use in import statement. 690 if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 { 691 return ipath[i+len("/vendor/"):] 692 } 693 if strings.HasPrefix(ipath, "vendor/") { 694 return ipath[len("vendor/"):] 695 } 696 return ipath 697 } 698 699 // loadExports returns the set of exported symbols in the package at dir. 700 // It returns nil on error or if the package name in dir does not match expectPackage. 701 var loadExports func(expectPackage, dir string) map[string]bool = loadExportsGoPath 702 703 func loadExportsGoPath(expectPackage, dir string) map[string]bool { 704 if Debug { 705 log.Printf("loading exports in dir %s (seeking package %s)", dir, expectPackage) 706 } 707 exports := make(map[string]bool) 708 709 ctx := build.Default 710 711 // ReadDir is like ioutil.ReadDir, but only returns *.go files 712 // and filters out _test.go files since they're not relevant 713 // and only slow things down. 714 ctx.ReadDir = func(dir string) (notTests []os.FileInfo, err error) { 715 all, err := ioutil.ReadDir(dir) 716 if err != nil { 717 return nil, err 718 } 719 notTests = all[:0] 720 for _, fi := range all { 721 name := fi.Name() 722 if strings.HasSuffix(name, ".go") && !strings.HasSuffix(name, "_test.go") { 723 notTests = append(notTests, fi) 724 } 725 } 726 return notTests, nil 727 } 728 729 files, err := ctx.ReadDir(dir) 730 if err != nil { 731 log.Print(err) 732 return nil 733 } 734 735 fset := token.NewFileSet() 736 737 for _, fi := range files { 738 match, err := ctx.MatchFile(dir, fi.Name()) 739 if err != nil || !match { 740 continue 741 } 742 fullFile := filepath.Join(dir, fi.Name()) 743 f, err := parser.ParseFile(fset, fullFile, nil, 0) 744 if err != nil { 745 if Debug { 746 log.Printf("Parsing %s: %v", fullFile, err) 747 } 748 return nil 749 } 750 pkgName := f.Name.Name 751 if pkgName == "documentation" { 752 // Special case from go/build.ImportDir, not 753 // handled by ctx.MatchFile. 754 continue 755 } 756 if pkgName != expectPackage { 757 if Debug { 758 log.Printf("scan of dir %v is not expected package %v (actually %v)", dir, expectPackage, pkgName) 759 } 760 return nil 761 } 762 for name := range f.Scope.Objects { 763 if ast.IsExported(name) { 764 exports[name] = true 765 } 766 } 767 } 768 769 if Debug { 770 exportList := make([]string, 0, len(exports)) 771 for k := range exports { 772 exportList = append(exportList, k) 773 } 774 sort.Strings(exportList) 775 log.Printf("loaded exports in dir %v (package %v): %v", dir, expectPackage, strings.Join(exportList, ", ")) 776 } 777 return exports 778 } 779 780 // findImport searches for a package with the given symbols. 781 // If no package is found, findImport returns ("", false, nil) 782 // 783 // This is declared as a variable rather than a function so goimports 784 // can be easily extended by adding a file with an init function. 785 // 786 // The rename value tells goimports whether to use the package name as 787 // a local qualifier in an import. For example, if findImports("pkg", 788 // "X") returns ("foo/bar", rename=true), then goimports adds the 789 // import line: 790 // import pkg "foo/bar" 791 // to satisfy uses of pkg.X in the file. 792 var findImport func(pkgName string, symbols map[string]bool, filename string) (foundPkg string, rename bool, err error) = findImportGoPath 793 794 // findImportGoPath is the normal implementation of findImport. 795 // (Some companies have their own internally.) 796 func findImportGoPath(pkgName string, symbols map[string]bool, filename string) (foundPkg string, rename bool, err error) { 797 if inTests { 798 testMu.RLock() 799 defer testMu.RUnlock() 800 } 801 802 pkgDir, err := filepath.Abs(filename) 803 if err != nil { 804 return "", false, err 805 } 806 pkgDir = filepath.Dir(pkgDir) 807 808 // Fast path for the standard library. 809 // In the common case we hopefully never have to scan the GOPATH, which can 810 // be slow with moving disks. 811 if pkg, ok := findImportStdlib(pkgName, symbols); ok { 812 return pkg, false, nil 813 } 814 if pkgName == "rand" && symbols["Read"] { 815 // Special-case rand.Read. 816 // 817 // If findImportStdlib didn't find it above, don't go 818 // searching for it, lest it find and pick math/rand 819 // in GOROOT (new as of Go 1.6) 820 // 821 // crypto/rand is the safer choice. 822 return "", false, nil 823 } 824 825 // TODO(sameer): look at the import lines for other Go files in the 826 // local directory, since the user is likely to import the same packages 827 // in the current Go file. Return rename=true when the other Go files 828 // use a renamed package that's also used in the current file. 829 830 // Read all the $GOPATH/src/.goimportsignore files before scanning directories. 831 populateIgnoreOnce.Do(populateIgnore) 832 833 // Start scanning the $GOROOT asynchronously, then run the 834 // GOPATH scan synchronously if needed, and then wait for the 835 // $GOROOT to finish. 836 // 837 // TODO(bradfitz): run each $GOPATH entry async. But nobody 838 // really has more than one anyway, so low priority. 839 scanGoRootOnce.Do(scanGoRoot) // async 840 if !fileInDir(filename, build.Default.GOROOT) { 841 scanGoPathOnce.Do(scanGoPath) // blocking 842 } 843 <-scanGoRootDone 844 845 // Find candidate packages, looking only at their directory names first. 846 var candidates []*pkg 847 for _, pkg := range dirScan { 848 if pkgIsCandidate(filename, pkgName, pkg) { 849 pkg.distance = distance(pkgDir, pkg.dir) 850 candidates = append(candidates, pkg) 851 } 852 } 853 854 // Sort the candidates by their import package length, 855 // assuming that shorter package names are better than long 856 // ones. Note that this sorts by the de-vendored name, so 857 // there's no "penalty" for vendoring. 858 sort.Sort(byDistanceOrImportPathShortLength(candidates)) 859 if Debug { 860 for i, pkg := range candidates { 861 log.Printf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), pkg.importPathShort, pkg.dir) 862 } 863 } 864 865 // Collect exports for packages with matching names. 866 867 done := make(chan struct{}) // closed when we find the answer 868 defer close(done) 869 870 rescv := make([]chan *pkg, len(candidates)) 871 for i := range candidates { 872 rescv[i] = make(chan *pkg) 873 } 874 const maxConcurrentPackageImport = 4 875 loadExportsSem := make(chan struct{}, maxConcurrentPackageImport) 876 877 go func() { 878 for i, pkg := range candidates { 879 select { 880 case loadExportsSem <- struct{}{}: 881 select { 882 case <-done: 883 return 884 default: 885 } 886 case <-done: 887 return 888 } 889 pkg := pkg 890 resc := rescv[i] 891 go func() { 892 if inTests { 893 testMu.RLock() 894 defer testMu.RUnlock() 895 } 896 defer func() { <-loadExportsSem }() 897 exports := loadExports(pkgName, pkg.dir) 898 899 // If it doesn't have the right 900 // symbols, send nil to mean no match. 901 for symbol := range symbols { 902 if !exports[symbol] { 903 pkg = nil 904 break 905 } 906 } 907 select { 908 case resc <- pkg: 909 case <-done: 910 } 911 }() 912 } 913 }() 914 for _, resc := range rescv { 915 pkg := <-resc 916 if pkg == nil { 917 continue 918 } 919 // If the package name in the source doesn't match the import path's base, 920 // return true so the rewriter adds a name (import foo "github.com/bar/go-foo") 921 needsRename := path.Base(pkg.importPath) != pkgName 922 return pkg.importPathShort, needsRename, nil 923 } 924 return "", false, nil 925 } 926 927 // pkgIsCandidate reports whether pkg is a candidate for satisfying the 928 // finding which package pkgIdent in the file named by filename is trying 929 // to refer to. 930 // 931 // This check is purely lexical and is meant to be as fast as possible 932 // because it's run over all $GOPATH directories to filter out poor 933 // candidates in order to limit the CPU and I/O later parsing the 934 // exports in candidate packages. 935 // 936 // filename is the file being formatted. 937 // pkgIdent is the package being searched for, like "client" (if 938 // searching for "client.New") 939 func pkgIsCandidate(filename, pkgIdent string, pkg *pkg) bool { 940 // Check "internal" and "vendor" visibility: 941 if !canUse(filename, pkg.dir) { 942 return false 943 } 944 945 // Speed optimization to minimize disk I/O: 946 // the last two components on disk must contain the 947 // package name somewhere. 948 // 949 // This permits mismatch naming like directory 950 // "go-foo" being package "foo", or "pkg.v3" being "pkg", 951 // or directory "google.golang.org/api/cloudbilling/v1" 952 // being package "cloudbilling", but doesn't 953 // permit a directory "foo" to be package 954 // "bar", which is strongly discouraged 955 // anyway. There's no reason goimports needs 956 // to be slow just to accomodate that. 957 lastTwo := lastTwoComponents(pkg.importPathShort) 958 if strings.Contains(lastTwo, pkgIdent) { 959 return true 960 } 961 if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(pkgIdent) { 962 lastTwo = lowerASCIIAndRemoveHyphen(lastTwo) 963 if strings.Contains(lastTwo, pkgIdent) { 964 return true 965 } 966 } 967 968 return false 969 } 970 971 func hasHyphenOrUpperASCII(s string) bool { 972 for i := 0; i < len(s); i++ { 973 b := s[i] 974 if b == '-' || ('A' <= b && b <= 'Z') { 975 return true 976 } 977 } 978 return false 979 } 980 981 func lowerASCIIAndRemoveHyphen(s string) (ret string) { 982 buf := make([]byte, 0, len(s)) 983 for i := 0; i < len(s); i++ { 984 b := s[i] 985 switch { 986 case b == '-': 987 continue 988 case 'A' <= b && b <= 'Z': 989 buf = append(buf, b+('a'-'A')) 990 default: 991 buf = append(buf, b) 992 } 993 } 994 return string(buf) 995 } 996 997 // canUse reports whether the package in dir is usable from filename, 998 // respecting the Go "internal" and "vendor" visibility rules. 999 func canUse(filename, dir string) bool { 1000 // Fast path check, before any allocations. If it doesn't contain vendor 1001 // or internal, it's not tricky: 1002 // Note that this can false-negative on directories like "notinternal", 1003 // but we check it correctly below. This is just a fast path. 1004 if !strings.Contains(dir, "vendor") && !strings.Contains(dir, "internal") { 1005 return true 1006 } 1007 1008 dirSlash := filepath.ToSlash(dir) 1009 if !strings.Contains(dirSlash, "/vendor/") && !strings.Contains(dirSlash, "/internal/") && !strings.HasSuffix(dirSlash, "/internal") { 1010 return true 1011 } 1012 // Vendor or internal directory only visible from children of parent. 1013 // That means the path from the current directory to the target directory 1014 // can contain ../vendor or ../internal but not ../foo/vendor or ../foo/internal 1015 // or bar/vendor or bar/internal. 1016 // After stripping all the leading ../, the only okay place to see vendor or internal 1017 // is at the very beginning of the path. 1018 absfile, err := filepath.Abs(filename) 1019 if err != nil { 1020 return false 1021 } 1022 absdir, err := filepath.Abs(dir) 1023 if err != nil { 1024 return false 1025 } 1026 rel, err := filepath.Rel(absfile, absdir) 1027 if err != nil { 1028 return false 1029 } 1030 relSlash := filepath.ToSlash(rel) 1031 if i := strings.LastIndex(relSlash, "../"); i >= 0 { 1032 relSlash = relSlash[i+len("../"):] 1033 } 1034 return !strings.Contains(relSlash, "/vendor/") && !strings.Contains(relSlash, "/internal/") && !strings.HasSuffix(relSlash, "/internal") 1035 } 1036 1037 // lastTwoComponents returns at most the last two path components 1038 // of v, using either / or \ as the path separator. 1039 func lastTwoComponents(v string) string { 1040 nslash := 0 1041 for i := len(v) - 1; i >= 0; i-- { 1042 if v[i] == '/' || v[i] == '\\' { 1043 nslash++ 1044 if nslash == 2 { 1045 return v[i:] 1046 } 1047 } 1048 } 1049 return v 1050 } 1051 1052 type visitFn func(node ast.Node) ast.Visitor 1053 1054 func (fn visitFn) Visit(node ast.Node) ast.Visitor { 1055 return fn(node) 1056 } 1057 1058 func findImportStdlib(shortPkg string, symbols map[string]bool) (importPath string, ok bool) { 1059 for symbol := range symbols { 1060 key := shortPkg + "." + symbol 1061 path := stdlib[key] 1062 if path == "" { 1063 if key == "rand.Read" { 1064 continue 1065 } 1066 return "", false 1067 } 1068 if importPath != "" && importPath != path { 1069 // Ambiguous. Symbols pointed to different things. 1070 return "", false 1071 } 1072 importPath = path 1073 } 1074 if importPath == "" && shortPkg == "rand" && symbols["Read"] { 1075 return "crypto/rand", true 1076 } 1077 return importPath, importPath != "" 1078 } 1079 1080 // fileInDir reports whether the provided file path looks like 1081 // it's in dir. (without hitting the filesystem) 1082 func fileInDir(file, dir string) bool { 1083 rest := strings.TrimPrefix(file, dir) 1084 if len(rest) == len(file) { 1085 // dir is not a prefix of file. 1086 return false 1087 } 1088 // Check for boundary: either nothing (file == dir), or a slash. 1089 return len(rest) == 0 || rest[0] == '/' || rest[0] == '\\' 1090 }