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  }