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.

1032 lines
28 KiB

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