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