github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/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  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"go/ast"
    13  	"go/build"
    14  	"go/parser"
    15  	"go/token"
    16  	"io/ioutil"
    17  	"os"
    18  	"path"
    19  	"path/filepath"
    20  	"reflect"
    21  	"sort"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  	"unicode"
    26  	"unicode/utf8"
    27  
    28  	"golang.org/x/tools/go/ast/astutil"
    29  	"golang.org/x/tools/internal/gocommand"
    30  	"golang.org/x/tools/internal/gopathwalk"
    31  )
    32  
    33  // importToGroup is a list of functions which map from an import path to
    34  // a group number.
    35  var importToGroup = []func(localPrefix, importPath string) (num int, ok bool){
    36  	func(localPrefix, importPath string) (num int, ok bool) {
    37  		if localPrefix == "" {
    38  			return
    39  		}
    40  		for _, p := range strings.Split(localPrefix, ",") {
    41  			if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath {
    42  				return 3, true
    43  			}
    44  		}
    45  		return
    46  	},
    47  	func(_, importPath string) (num int, ok bool) {
    48  		if strings.HasPrefix(importPath, "appengine") {
    49  			return 2, true
    50  		}
    51  		return
    52  	},
    53  	func(_, importPath string) (num int, ok bool) {
    54  		firstComponent := strings.Split(importPath, "/")[0]
    55  		if strings.Contains(firstComponent, ".") {
    56  			return 1, true
    57  		}
    58  		return
    59  	},
    60  }
    61  
    62  func importGroup(localPrefix, importPath string) int {
    63  	for _, fn := range importToGroup {
    64  		if n, ok := fn(localPrefix, importPath); ok {
    65  			return n
    66  		}
    67  	}
    68  	return 0
    69  }
    70  
    71  type ImportFixType int
    72  
    73  const (
    74  	AddImport ImportFixType = iota
    75  	DeleteImport
    76  	SetImportName
    77  )
    78  
    79  type ImportFix struct {
    80  	// StmtInfo represents the import statement this fix will add, remove, or change.
    81  	StmtInfo ImportInfo
    82  	// IdentName is the identifier that this fix will add or remove.
    83  	IdentName string
    84  	// FixType is the type of fix this is (AddImport, DeleteImport, SetImportName).
    85  	FixType   ImportFixType
    86  	Relevance float64 // see pkg
    87  }
    88  
    89  // An ImportInfo represents a single import statement.
    90  type ImportInfo struct {
    91  	ImportPath string // import path, e.g. "crypto/rand".
    92  	Name       string // import name, e.g. "crand", or "" if none.
    93  }
    94  
    95  // A packageInfo represents what's known about a package.
    96  type packageInfo struct {
    97  	name    string          // real package name, if known.
    98  	exports map[string]bool // known exports.
    99  }
   100  
   101  // parseOtherFiles parses all the Go files in srcDir except filename, including
   102  // test files if filename looks like a test.
   103  func parseOtherFiles(fset *token.FileSet, srcDir, filename string) []*ast.File {
   104  	// This could use go/packages but it doesn't buy much, and it fails
   105  	// with https://golang.org/issue/26296 in LoadFiles mode in some cases.
   106  	considerTests := strings.HasSuffix(filename, "_test.go")
   107  
   108  	fileBase := filepath.Base(filename)
   109  	packageFileInfos, err := ioutil.ReadDir(srcDir)
   110  	if err != nil {
   111  		return nil
   112  	}
   113  
   114  	var files []*ast.File
   115  	for _, fi := range packageFileInfos {
   116  		if fi.Name() == fileBase || !strings.HasSuffix(fi.Name(), ".go") {
   117  			continue
   118  		}
   119  		if !considerTests && strings.HasSuffix(fi.Name(), "_test.go") {
   120  			continue
   121  		}
   122  
   123  		f, err := parser.ParseFile(fset, filepath.Join(srcDir, fi.Name()), nil, 0)
   124  		if err != nil {
   125  			continue
   126  		}
   127  
   128  		files = append(files, f)
   129  	}
   130  
   131  	return files
   132  }
   133  
   134  // addGlobals puts the names of package vars into the provided map.
   135  func addGlobals(f *ast.File, globals map[string]bool) {
   136  	for _, decl := range f.Decls {
   137  		genDecl, ok := decl.(*ast.GenDecl)
   138  		if !ok {
   139  			continue
   140  		}
   141  
   142  		for _, spec := range genDecl.Specs {
   143  			valueSpec, ok := spec.(*ast.ValueSpec)
   144  			if !ok {
   145  				continue
   146  			}
   147  			globals[valueSpec.Names[0].Name] = true
   148  		}
   149  	}
   150  }
   151  
   152  // collectReferences builds a map of selector expressions, from
   153  // left hand side (X) to a set of right hand sides (Sel).
   154  func collectReferences(f *ast.File) references {
   155  	refs := references{}
   156  
   157  	var visitor visitFn
   158  	visitor = func(node ast.Node) ast.Visitor {
   159  		if node == nil {
   160  			return visitor
   161  		}
   162  		switch v := node.(type) {
   163  		case *ast.SelectorExpr:
   164  			xident, ok := v.X.(*ast.Ident)
   165  			if !ok {
   166  				break
   167  			}
   168  			if xident.Obj != nil {
   169  				// If the parser can resolve it, it's not a package ref.
   170  				break
   171  			}
   172  			if !ast.IsExported(v.Sel.Name) {
   173  				// Whatever this is, it's not exported from a package.
   174  				break
   175  			}
   176  			pkgName := xident.Name
   177  			r := refs[pkgName]
   178  			if r == nil {
   179  				r = make(map[string]bool)
   180  				refs[pkgName] = r
   181  			}
   182  			r[v.Sel.Name] = true
   183  		}
   184  		return visitor
   185  	}
   186  	ast.Walk(visitor, f)
   187  	return refs
   188  }
   189  
   190  // collectImports returns all the imports in f.
   191  // Unnamed imports (., _) and "C" are ignored.
   192  func collectImports(f *ast.File) []*ImportInfo {
   193  	var imports []*ImportInfo
   194  	for _, imp := range f.Imports {
   195  		var name string
   196  		if imp.Name != nil {
   197  			name = imp.Name.Name
   198  		}
   199  		if imp.Path.Value == `"C"` || name == "_" || name == "." {
   200  			continue
   201  		}
   202  		path := strings.Trim(imp.Path.Value, `"`)
   203  		imports = append(imports, &ImportInfo{
   204  			Name:       name,
   205  			ImportPath: path,
   206  		})
   207  	}
   208  	return imports
   209  }
   210  
   211  // findMissingImport searches pass's candidates for an import that provides
   212  // pkg, containing all of syms.
   213  func (p *pass) findMissingImport(pkg string, syms map[string]bool) *ImportInfo {
   214  	for _, candidate := range p.candidates {
   215  		pkgInfo, ok := p.knownPackages[candidate.ImportPath]
   216  		if !ok {
   217  			continue
   218  		}
   219  		if p.importIdentifier(candidate) != pkg {
   220  			continue
   221  		}
   222  
   223  		allFound := true
   224  		for right := range syms {
   225  			if !pkgInfo.exports[right] {
   226  				allFound = false
   227  				break
   228  			}
   229  		}
   230  
   231  		if allFound {
   232  			return candidate
   233  		}
   234  	}
   235  	return nil
   236  }
   237  
   238  // references is set of references found in a Go file. The first map key is the
   239  // left hand side of a selector expression, the second key is the right hand
   240  // side, and the value should always be true.
   241  type references map[string]map[string]bool
   242  
   243  // A pass contains all the inputs and state necessary to fix a file's imports.
   244  // It can be modified in some ways during use; see comments below.
   245  type pass struct {
   246  	// Inputs. These must be set before a call to load, and not modified after.
   247  	fset                 *token.FileSet // fset used to parse f and its siblings.
   248  	f                    *ast.File      // the file being fixed.
   249  	srcDir               string         // the directory containing f.
   250  	env                  *ProcessEnv    // the environment to use for go commands, etc.
   251  	loadRealPackageNames bool           // if true, load package names from disk rather than guessing them.
   252  	otherFiles           []*ast.File    // sibling files.
   253  
   254  	// Intermediate state, generated by load.
   255  	existingImports map[string]*ImportInfo
   256  	allRefs         references
   257  	missingRefs     references
   258  
   259  	// Inputs to fix. These can be augmented between successive fix calls.
   260  	lastTry       bool                    // indicates that this is the last call and fix should clean up as best it can.
   261  	candidates    []*ImportInfo           // candidate imports in priority order.
   262  	knownPackages map[string]*packageInfo // information about all known packages.
   263  }
   264  
   265  // loadPackageNames saves the package names for everything referenced by imports.
   266  func (p *pass) loadPackageNames(imports []*ImportInfo) error {
   267  	if p.env.Logf != nil {
   268  		p.env.Logf("loading package names for %v packages", len(imports))
   269  		defer func() {
   270  			p.env.Logf("done loading package names for %v packages", len(imports))
   271  		}()
   272  	}
   273  	var unknown []string
   274  	for _, imp := range imports {
   275  		if _, ok := p.knownPackages[imp.ImportPath]; ok {
   276  			continue
   277  		}
   278  		unknown = append(unknown, imp.ImportPath)
   279  	}
   280  
   281  	resolver, err := p.env.GetResolver()
   282  	if err != nil {
   283  		return err
   284  	}
   285  
   286  	names, err := resolver.loadPackageNames(unknown, p.srcDir)
   287  	if err != nil {
   288  		return err
   289  	}
   290  
   291  	for path, name := range names {
   292  		p.knownPackages[path] = &packageInfo{
   293  			name:    name,
   294  			exports: map[string]bool{},
   295  		}
   296  	}
   297  	return nil
   298  }
   299  
   300  // importIdentifier returns the identifier that imp will introduce. It will
   301  // guess if the package name has not been loaded, e.g. because the source
   302  // is not available.
   303  func (p *pass) importIdentifier(imp *ImportInfo) string {
   304  	if imp.Name != "" {
   305  		return imp.Name
   306  	}
   307  	known := p.knownPackages[imp.ImportPath]
   308  	if known != nil && known.name != "" {
   309  		return known.name
   310  	}
   311  	return ImportPathToAssumedName(imp.ImportPath)
   312  }
   313  
   314  // load reads in everything necessary to run a pass, and reports whether the
   315  // file already has all the imports it needs. It fills in p.missingRefs with the
   316  // file's missing symbols, if any, or removes unused imports if not.
   317  func (p *pass) load() ([]*ImportFix, bool) {
   318  	p.knownPackages = map[string]*packageInfo{}
   319  	p.missingRefs = references{}
   320  	p.existingImports = map[string]*ImportInfo{}
   321  
   322  	// Load basic information about the file in question.
   323  	p.allRefs = collectReferences(p.f)
   324  
   325  	// Load stuff from other files in the same package:
   326  	// global variables so we know they don't need resolving, and imports
   327  	// that we might want to mimic.
   328  	globals := map[string]bool{}
   329  	for _, otherFile := range p.otherFiles {
   330  		// Don't load globals from files that are in the same directory
   331  		// but a different package. Using them to suggest imports is OK.
   332  		if p.f.Name.Name == otherFile.Name.Name {
   333  			addGlobals(otherFile, globals)
   334  		}
   335  		p.candidates = append(p.candidates, collectImports(otherFile)...)
   336  	}
   337  
   338  	// Resolve all the import paths we've seen to package names, and store
   339  	// f's imports by the identifier they introduce.
   340  	imports := collectImports(p.f)
   341  	if p.loadRealPackageNames {
   342  		err := p.loadPackageNames(append(imports, p.candidates...))
   343  		if err != nil {
   344  			if p.env.Logf != nil {
   345  				p.env.Logf("loading package names: %v", err)
   346  			}
   347  			return nil, false
   348  		}
   349  	}
   350  	for _, imp := range imports {
   351  		p.existingImports[p.importIdentifier(imp)] = imp
   352  	}
   353  
   354  	// Find missing references.
   355  	for left, rights := range p.allRefs {
   356  		if globals[left] {
   357  			continue
   358  		}
   359  		_, ok := p.existingImports[left]
   360  		if !ok {
   361  			p.missingRefs[left] = rights
   362  			continue
   363  		}
   364  	}
   365  	if len(p.missingRefs) != 0 {
   366  		return nil, false
   367  	}
   368  
   369  	return p.fix()
   370  }
   371  
   372  // fix attempts to satisfy missing imports using p.candidates. If it finds
   373  // everything, or if p.lastTry is true, it updates fixes to add the imports it found,
   374  // delete anything unused, and update import names, and returns true.
   375  func (p *pass) fix() ([]*ImportFix, bool) {
   376  	// Find missing imports.
   377  	var selected []*ImportInfo
   378  	for left, rights := range p.missingRefs {
   379  		if imp := p.findMissingImport(left, rights); imp != nil {
   380  			selected = append(selected, imp)
   381  		}
   382  	}
   383  
   384  	if !p.lastTry && len(selected) != len(p.missingRefs) {
   385  		return nil, false
   386  	}
   387  
   388  	// Found everything, or giving up. Add the new imports and remove any unused.
   389  	var fixes []*ImportFix
   390  	for _, imp := range p.existingImports {
   391  		// We deliberately ignore globals here, because we can't be sure
   392  		// they're in the same package. People do things like put multiple
   393  		// main packages in the same directory, and we don't want to
   394  		// remove imports if they happen to have the same name as a var in
   395  		// a different package.
   396  		if _, ok := p.allRefs[p.importIdentifier(imp)]; !ok {
   397  			fixes = append(fixes, &ImportFix{
   398  				StmtInfo:  *imp,
   399  				IdentName: p.importIdentifier(imp),
   400  				FixType:   DeleteImport,
   401  			})
   402  			continue
   403  		}
   404  
   405  		// An existing import may need to update its import name to be correct.
   406  		if name := p.importSpecName(imp); name != imp.Name {
   407  			fixes = append(fixes, &ImportFix{
   408  				StmtInfo: ImportInfo{
   409  					Name:       name,
   410  					ImportPath: imp.ImportPath,
   411  				},
   412  				IdentName: p.importIdentifier(imp),
   413  				FixType:   SetImportName,
   414  			})
   415  		}
   416  	}
   417  
   418  	for _, imp := range selected {
   419  		fixes = append(fixes, &ImportFix{
   420  			StmtInfo: ImportInfo{
   421  				Name:       p.importSpecName(imp),
   422  				ImportPath: imp.ImportPath,
   423  			},
   424  			IdentName: p.importIdentifier(imp),
   425  			FixType:   AddImport,
   426  		})
   427  	}
   428  
   429  	return fixes, true
   430  }
   431  
   432  // importSpecName gets the import name of imp in the import spec.
   433  //
   434  // When the import identifier matches the assumed import name, the import name does
   435  // not appear in the import spec.
   436  func (p *pass) importSpecName(imp *ImportInfo) string {
   437  	// If we did not load the real package names, or the name is already set,
   438  	// we just return the existing name.
   439  	if !p.loadRealPackageNames || imp.Name != "" {
   440  		return imp.Name
   441  	}
   442  
   443  	ident := p.importIdentifier(imp)
   444  	if ident == ImportPathToAssumedName(imp.ImportPath) {
   445  		return "" // ident not needed since the assumed and real names are the same.
   446  	}
   447  	return ident
   448  }
   449  
   450  // apply will perform the fixes on f in order.
   451  func apply(fset *token.FileSet, f *ast.File, fixes []*ImportFix) {
   452  	for _, fix := range fixes {
   453  		switch fix.FixType {
   454  		case DeleteImport:
   455  			astutil.DeleteNamedImport(fset, f, fix.StmtInfo.Name, fix.StmtInfo.ImportPath)
   456  		case AddImport:
   457  			astutil.AddNamedImport(fset, f, fix.StmtInfo.Name, fix.StmtInfo.ImportPath)
   458  		case SetImportName:
   459  			// Find the matching import path and change the name.
   460  			for _, spec := range f.Imports {
   461  				path := strings.Trim(spec.Path.Value, `"`)
   462  				if path == fix.StmtInfo.ImportPath {
   463  					spec.Name = &ast.Ident{
   464  						Name:    fix.StmtInfo.Name,
   465  						NamePos: spec.Pos(),
   466  					}
   467  				}
   468  			}
   469  		}
   470  	}
   471  }
   472  
   473  // assumeSiblingImportsValid assumes that siblings' use of packages is valid,
   474  // adding the exports they use.
   475  func (p *pass) assumeSiblingImportsValid() {
   476  	for _, f := range p.otherFiles {
   477  		refs := collectReferences(f)
   478  		imports := collectImports(f)
   479  		importsByName := map[string]*ImportInfo{}
   480  		for _, imp := range imports {
   481  			importsByName[p.importIdentifier(imp)] = imp
   482  		}
   483  		for left, rights := range refs {
   484  			if imp, ok := importsByName[left]; ok {
   485  				if m, ok := stdlib[imp.ImportPath]; ok {
   486  					// We have the stdlib in memory; no need to guess.
   487  					rights = copyExports(m)
   488  				}
   489  				p.addCandidate(imp, &packageInfo{
   490  					// no name; we already know it.
   491  					exports: rights,
   492  				})
   493  			}
   494  		}
   495  	}
   496  }
   497  
   498  // addCandidate adds a candidate import to p, and merges in the information
   499  // in pkg.
   500  func (p *pass) addCandidate(imp *ImportInfo, pkg *packageInfo) {
   501  	p.candidates = append(p.candidates, imp)
   502  	if existing, ok := p.knownPackages[imp.ImportPath]; ok {
   503  		if existing.name == "" {
   504  			existing.name = pkg.name
   505  		}
   506  		for export := range pkg.exports {
   507  			existing.exports[export] = true
   508  		}
   509  	} else {
   510  		p.knownPackages[imp.ImportPath] = pkg
   511  	}
   512  }
   513  
   514  // fixImports adds and removes imports from f so that all its references are
   515  // satisfied and there are no unused imports.
   516  //
   517  // This is declared as a variable rather than a function so goimports can
   518  // easily be extended by adding a file with an init function.
   519  var fixImports = fixImportsDefault
   520  
   521  func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) error {
   522  	fixes, err := getFixes(fset, f, filename, env)
   523  	if err != nil {
   524  		return err
   525  	}
   526  	apply(fset, f, fixes)
   527  	return err
   528  }
   529  
   530  // getFixes gets the import fixes that need to be made to f in order to fix the imports.
   531  // It does not modify the ast.
   532  func getFixes(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) ([]*ImportFix, error) {
   533  	abs, err := filepath.Abs(filename)
   534  	if err != nil {
   535  		return nil, err
   536  	}
   537  	srcDir := filepath.Dir(abs)
   538  	if env.Logf != nil {
   539  		env.Logf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir)
   540  	}
   541  
   542  	// First pass: looking only at f, and using the naive algorithm to
   543  	// derive package names from import paths, see if the file is already
   544  	// complete. We can't add any imports yet, because we don't know
   545  	// if missing references are actually package vars.
   546  	p := &pass{fset: fset, f: f, srcDir: srcDir, env: env}
   547  	if fixes, done := p.load(); done {
   548  		return fixes, nil
   549  	}
   550  
   551  	otherFiles := parseOtherFiles(fset, srcDir, filename)
   552  
   553  	// Second pass: add information from other files in the same package,
   554  	// like their package vars and imports.
   555  	p.otherFiles = otherFiles
   556  	if fixes, done := p.load(); done {
   557  		return fixes, nil
   558  	}
   559  
   560  	// Now we can try adding imports from the stdlib.
   561  	p.assumeSiblingImportsValid()
   562  	addStdlibCandidates(p, p.missingRefs)
   563  	if fixes, done := p.fix(); done {
   564  		return fixes, nil
   565  	}
   566  
   567  	// Third pass: get real package names where we had previously used
   568  	// the naive algorithm.
   569  	p = &pass{fset: fset, f: f, srcDir: srcDir, env: env}
   570  	p.loadRealPackageNames = true
   571  	p.otherFiles = otherFiles
   572  	if fixes, done := p.load(); done {
   573  		return fixes, nil
   574  	}
   575  
   576  	if err := addStdlibCandidates(p, p.missingRefs); err != nil {
   577  		return nil, err
   578  	}
   579  	p.assumeSiblingImportsValid()
   580  	if fixes, done := p.fix(); done {
   581  		return fixes, nil
   582  	}
   583  
   584  	// Go look for candidates in $GOPATH, etc. We don't necessarily load
   585  	// the real exports of sibling imports, so keep assuming their contents.
   586  	if err := addExternalCandidates(p, p.missingRefs, filename); err != nil {
   587  		return nil, err
   588  	}
   589  
   590  	p.lastTry = true
   591  	fixes, _ := p.fix()
   592  	return fixes, nil
   593  }
   594  
   595  // MaxRelevance is the highest relevance, used for the standard library.
   596  // Chosen arbitrarily to match pre-existing gopls code.
   597  const MaxRelevance = 7.0
   598  
   599  // getCandidatePkgs works with the passed callback to find all acceptable packages.
   600  // It deduplicates by import path, and uses a cached stdlib rather than reading
   601  // from disk.
   602  func getCandidatePkgs(ctx context.Context, wrappedCallback *scanCallback, filename, filePkg string, env *ProcessEnv) error {
   603  	notSelf := func(p *pkg) bool {
   604  		return p.packageName != filePkg || p.dir != filepath.Dir(filename)
   605  	}
   606  	goenv, err := env.goEnv()
   607  	if err != nil {
   608  		return err
   609  	}
   610  
   611  	var mu sync.Mutex // to guard asynchronous access to dupCheck
   612  	dupCheck := map[string]struct{}{}
   613  
   614  	// Start off with the standard library.
   615  	for importPath, exports := range stdlib {
   616  		p := &pkg{
   617  			dir:             filepath.Join(goenv["GOROOT"], "src", importPath),
   618  			importPathShort: importPath,
   619  			packageName:     path.Base(importPath),
   620  			relevance:       MaxRelevance,
   621  		}
   622  		dupCheck[importPath] = struct{}{}
   623  		if notSelf(p) && wrappedCallback.dirFound(p) && wrappedCallback.packageNameLoaded(p) {
   624  			wrappedCallback.exportsLoaded(p, exports)
   625  		}
   626  	}
   627  
   628  	scanFilter := &scanCallback{
   629  		rootFound: func(root gopathwalk.Root) bool {
   630  			// Exclude goroot results -- getting them is relatively expensive, not cached,
   631  			// and generally redundant with the in-memory version.
   632  			return root.Type != gopathwalk.RootGOROOT && wrappedCallback.rootFound(root)
   633  		},
   634  		dirFound: wrappedCallback.dirFound,
   635  		packageNameLoaded: func(pkg *pkg) bool {
   636  			mu.Lock()
   637  			defer mu.Unlock()
   638  			if _, ok := dupCheck[pkg.importPathShort]; ok {
   639  				return false
   640  			}
   641  			dupCheck[pkg.importPathShort] = struct{}{}
   642  			return notSelf(pkg) && wrappedCallback.packageNameLoaded(pkg)
   643  		},
   644  		exportsLoaded: func(pkg *pkg, exports []string) {
   645  			// If we're an x_test, load the package under test's test variant.
   646  			if strings.HasSuffix(filePkg, "_test") && pkg.dir == filepath.Dir(filename) {
   647  				var err error
   648  				_, exports, err = loadExportsFromFiles(ctx, env, pkg.dir, true)
   649  				if err != nil {
   650  					return
   651  				}
   652  			}
   653  			wrappedCallback.exportsLoaded(pkg, exports)
   654  		},
   655  	}
   656  	resolver, err := env.GetResolver()
   657  	if err != nil {
   658  		return err
   659  	}
   660  	return resolver.scan(ctx, scanFilter)
   661  }
   662  
   663  func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) (map[string]float64, error) {
   664  	result := make(map[string]float64)
   665  	resolver, err := env.GetResolver()
   666  	if err != nil {
   667  		return nil, err
   668  	}
   669  	for _, path := range paths {
   670  		result[path] = resolver.scoreImportPath(ctx, path)
   671  	}
   672  	return result, nil
   673  }
   674  
   675  func PrimeCache(ctx context.Context, env *ProcessEnv) error {
   676  	// Fully scan the disk for directories, but don't actually read any Go files.
   677  	callback := &scanCallback{
   678  		rootFound: func(gopathwalk.Root) bool {
   679  			return true
   680  		},
   681  		dirFound: func(pkg *pkg) bool {
   682  			return false
   683  		},
   684  		packageNameLoaded: func(pkg *pkg) bool {
   685  			return false
   686  		},
   687  	}
   688  	return getCandidatePkgs(ctx, callback, "", "", env)
   689  }
   690  
   691  func candidateImportName(pkg *pkg) string {
   692  	if ImportPathToAssumedName(pkg.importPathShort) != pkg.packageName {
   693  		return pkg.packageName
   694  	}
   695  	return ""
   696  }
   697  
   698  // GetAllCandidates calls wrapped for each package whose name starts with
   699  // searchPrefix, and can be imported from filename with the package name filePkg.
   700  //
   701  // Beware that the wrapped function may be called multiple times concurrently.
   702  // TODO(adonovan): encapsulate the concurrency.
   703  func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
   704  	callback := &scanCallback{
   705  		rootFound: func(gopathwalk.Root) bool {
   706  			return true
   707  		},
   708  		dirFound: func(pkg *pkg) bool {
   709  			if !canUse(filename, pkg.dir) {
   710  				return false
   711  			}
   712  			// Try the assumed package name first, then a simpler path match
   713  			// in case of packages named vN, which are not uncommon.
   714  			return strings.HasPrefix(ImportPathToAssumedName(pkg.importPathShort), searchPrefix) ||
   715  				strings.HasPrefix(path.Base(pkg.importPathShort), searchPrefix)
   716  		},
   717  		packageNameLoaded: func(pkg *pkg) bool {
   718  			if !strings.HasPrefix(pkg.packageName, searchPrefix) {
   719  				return false
   720  			}
   721  			wrapped(ImportFix{
   722  				StmtInfo: ImportInfo{
   723  					ImportPath: pkg.importPathShort,
   724  					Name:       candidateImportName(pkg),
   725  				},
   726  				IdentName: pkg.packageName,
   727  				FixType:   AddImport,
   728  				Relevance: pkg.relevance,
   729  			})
   730  			return false
   731  		},
   732  	}
   733  	return getCandidatePkgs(ctx, callback, filename, filePkg, env)
   734  }
   735  
   736  // GetImportPaths calls wrapped for each package whose import path starts with
   737  // searchPrefix, and can be imported from filename with the package name filePkg.
   738  func GetImportPaths(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
   739  	callback := &scanCallback{
   740  		rootFound: func(gopathwalk.Root) bool {
   741  			return true
   742  		},
   743  		dirFound: func(pkg *pkg) bool {
   744  			if !canUse(filename, pkg.dir) {
   745  				return false
   746  			}
   747  			return strings.HasPrefix(pkg.importPathShort, searchPrefix)
   748  		},
   749  		packageNameLoaded: func(pkg *pkg) bool {
   750  			wrapped(ImportFix{
   751  				StmtInfo: ImportInfo{
   752  					ImportPath: pkg.importPathShort,
   753  					Name:       candidateImportName(pkg),
   754  				},
   755  				IdentName: pkg.packageName,
   756  				FixType:   AddImport,
   757  				Relevance: pkg.relevance,
   758  			})
   759  			return false
   760  		},
   761  	}
   762  	return getCandidatePkgs(ctx, callback, filename, filePkg, env)
   763  }
   764  
   765  // A PackageExport is a package and its exports.
   766  type PackageExport struct {
   767  	Fix     *ImportFix
   768  	Exports []string
   769  }
   770  
   771  // GetPackageExports returns all known packages with name pkg and their exports.
   772  func GetPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error {
   773  	callback := &scanCallback{
   774  		rootFound: func(gopathwalk.Root) bool {
   775  			return true
   776  		},
   777  		dirFound: func(pkg *pkg) bool {
   778  			return pkgIsCandidate(filename, references{searchPkg: nil}, pkg)
   779  		},
   780  		packageNameLoaded: func(pkg *pkg) bool {
   781  			return pkg.packageName == searchPkg
   782  		},
   783  		exportsLoaded: func(pkg *pkg, exports []string) {
   784  			sort.Strings(exports)
   785  			wrapped(PackageExport{
   786  				Fix: &ImportFix{
   787  					StmtInfo: ImportInfo{
   788  						ImportPath: pkg.importPathShort,
   789  						Name:       candidateImportName(pkg),
   790  					},
   791  					IdentName: pkg.packageName,
   792  					FixType:   AddImport,
   793  					Relevance: pkg.relevance,
   794  				},
   795  				Exports: exports,
   796  			})
   797  		},
   798  	}
   799  	return getCandidatePkgs(ctx, callback, filename, filePkg, env)
   800  }
   801  
   802  var requiredGoEnvVars = []string{"GO111MODULE", "GOFLAGS", "GOINSECURE", "GOMOD", "GOMODCACHE", "GONOPROXY", "GONOSUMDB", "GOPATH", "GOPROXY", "GOROOT", "GOSUMDB", "GOWORK"}
   803  
   804  // ProcessEnv contains environment variables and settings that affect the use of
   805  // the go command, the go/build package, etc.
   806  type ProcessEnv struct {
   807  	GocmdRunner *gocommand.Runner
   808  
   809  	BuildFlags []string
   810  	ModFlag    string
   811  	ModFile    string
   812  
   813  	// SkipPathInScan returns true if the path should be skipped from scans of
   814  	// the RootCurrentModule root type. The function argument is a clean,
   815  	// absolute path.
   816  	SkipPathInScan func(string) bool
   817  
   818  	// Env overrides the OS environment, and can be used to specify
   819  	// GOPROXY, GO111MODULE, etc. PATH cannot be set here, because
   820  	// exec.Command will not honor it.
   821  	// Specifying all of RequiredGoEnvVars avoids a call to `go env`.
   822  	Env map[string]string
   823  
   824  	WorkingDir string
   825  
   826  	// If Logf is non-nil, debug logging is enabled through this function.
   827  	Logf func(format string, args ...interface{})
   828  
   829  	initialized bool
   830  
   831  	resolver Resolver
   832  }
   833  
   834  func (e *ProcessEnv) goEnv() (map[string]string, error) {
   835  	if err := e.init(); err != nil {
   836  		return nil, err
   837  	}
   838  	return e.Env, nil
   839  }
   840  
   841  func (e *ProcessEnv) matchFile(dir, name string) (bool, error) {
   842  	bctx, err := e.buildContext()
   843  	if err != nil {
   844  		return false, err
   845  	}
   846  	return bctx.MatchFile(dir, name)
   847  }
   848  
   849  // CopyConfig copies the env's configuration into a new env.
   850  func (e *ProcessEnv) CopyConfig() *ProcessEnv {
   851  	copy := &ProcessEnv{
   852  		GocmdRunner: e.GocmdRunner,
   853  		initialized: e.initialized,
   854  		BuildFlags:  e.BuildFlags,
   855  		Logf:        e.Logf,
   856  		WorkingDir:  e.WorkingDir,
   857  		resolver:    nil,
   858  		Env:         map[string]string{},
   859  	}
   860  	for k, v := range e.Env {
   861  		copy.Env[k] = v
   862  	}
   863  	return copy
   864  }
   865  
   866  func (e *ProcessEnv) init() error {
   867  	if e.initialized {
   868  		return nil
   869  	}
   870  
   871  	foundAllRequired := true
   872  	for _, k := range requiredGoEnvVars {
   873  		if _, ok := e.Env[k]; !ok {
   874  			foundAllRequired = false
   875  			break
   876  		}
   877  	}
   878  	if foundAllRequired {
   879  		e.initialized = true
   880  		return nil
   881  	}
   882  
   883  	if e.Env == nil {
   884  		e.Env = map[string]string{}
   885  	}
   886  
   887  	goEnv := map[string]string{}
   888  	stdout, err := e.invokeGo(context.TODO(), "env", append([]string{"-json"}, requiredGoEnvVars...)...)
   889  	if err != nil {
   890  		return err
   891  	}
   892  	if err := json.Unmarshal(stdout.Bytes(), &goEnv); err != nil {
   893  		return err
   894  	}
   895  	for k, v := range goEnv {
   896  		e.Env[k] = v
   897  	}
   898  	e.initialized = true
   899  	return nil
   900  }
   901  
   902  func (e *ProcessEnv) env() []string {
   903  	var env []string // the gocommand package will prepend os.Environ.
   904  	for k, v := range e.Env {
   905  		env = append(env, k+"="+v)
   906  	}
   907  	return env
   908  }
   909  
   910  func (e *ProcessEnv) GetResolver() (Resolver, error) {
   911  	if e.resolver != nil {
   912  		return e.resolver, nil
   913  	}
   914  	if err := e.init(); err != nil {
   915  		return nil, err
   916  	}
   917  	if len(e.Env["GOMOD"]) == 0 && len(e.Env["GOWORK"]) == 0 {
   918  		e.resolver = newGopathResolver(e)
   919  		return e.resolver, nil
   920  	}
   921  	e.resolver = newModuleResolver(e)
   922  	return e.resolver, nil
   923  }
   924  
   925  func (e *ProcessEnv) buildContext() (*build.Context, error) {
   926  	ctx := build.Default
   927  	goenv, err := e.goEnv()
   928  	if err != nil {
   929  		return nil, err
   930  	}
   931  	ctx.GOROOT = goenv["GOROOT"]
   932  	ctx.GOPATH = goenv["GOPATH"]
   933  
   934  	// As of Go 1.14, build.Context has a Dir field
   935  	// (see golang.org/issue/34860).
   936  	// Populate it only if present.
   937  	rc := reflect.ValueOf(&ctx).Elem()
   938  	dir := rc.FieldByName("Dir")
   939  	if dir.IsValid() && dir.Kind() == reflect.String {
   940  		dir.SetString(e.WorkingDir)
   941  	}
   942  
   943  	// Since Go 1.11, go/build.Context.Import may invoke 'go list' depending on
   944  	// the value in GO111MODULE in the process's environment. We always want to
   945  	// run in GOPATH mode when calling Import, so we need to prevent this from
   946  	// happening. In Go 1.16, GO111MODULE defaults to "on", so this problem comes
   947  	// up more frequently.
   948  	//
   949  	// HACK: setting any of the Context I/O hooks prevents Import from invoking
   950  	// 'go list', regardless of GO111MODULE. This is undocumented, but it's
   951  	// unlikely to change before GOPATH support is removed.
   952  	ctx.ReadDir = ioutil.ReadDir
   953  
   954  	return &ctx, nil
   955  }
   956  
   957  func (e *ProcessEnv) invokeGo(ctx context.Context, verb string, args ...string) (*bytes.Buffer, error) {
   958  	inv := gocommand.Invocation{
   959  		Verb:       verb,
   960  		Args:       args,
   961  		BuildFlags: e.BuildFlags,
   962  		Env:        e.env(),
   963  		Logf:       e.Logf,
   964  		WorkingDir: e.WorkingDir,
   965  	}
   966  	return e.GocmdRunner.Run(ctx, inv)
   967  }
   968  
   969  func addStdlibCandidates(pass *pass, refs references) error {
   970  	goenv, err := pass.env.goEnv()
   971  	if err != nil {
   972  		return err
   973  	}
   974  	add := func(pkg string) {
   975  		// Prevent self-imports.
   976  		if path.Base(pkg) == pass.f.Name.Name && filepath.Join(goenv["GOROOT"], "src", pkg) == pass.srcDir {
   977  			return
   978  		}
   979  		exports := copyExports(stdlib[pkg])
   980  		pass.addCandidate(
   981  			&ImportInfo{ImportPath: pkg},
   982  			&packageInfo{name: path.Base(pkg), exports: exports})
   983  	}
   984  	for left := range refs {
   985  		if left == "rand" {
   986  			// Make sure we try crypto/rand before math/rand.
   987  			add("crypto/rand")
   988  			add("math/rand")
   989  			continue
   990  		}
   991  		for importPath := range stdlib {
   992  			if path.Base(importPath) == left {
   993  				add(importPath)
   994  			}
   995  		}
   996  	}
   997  	return nil
   998  }
   999  
  1000  // A Resolver does the build-system-specific parts of goimports.
  1001  type Resolver interface {
  1002  	// loadPackageNames loads the package names in importPaths.
  1003  	loadPackageNames(importPaths []string, srcDir string) (map[string]string, error)
  1004  	// scan works with callback to search for packages. See scanCallback for details.
  1005  	scan(ctx context.Context, callback *scanCallback) error
  1006  	// loadExports returns the set of exported symbols in the package at dir.
  1007  	// loadExports may be called concurrently.
  1008  	loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error)
  1009  	// scoreImportPath returns the relevance for an import path.
  1010  	scoreImportPath(ctx context.Context, path string) float64
  1011  
  1012  	ClearForNewScan()
  1013  }
  1014  
  1015  // A scanCallback controls a call to scan and receives its results.
  1016  // In general, minor errors will be silently discarded; a user should not
  1017  // expect to receive a full series of calls for everything.
  1018  type scanCallback struct {
  1019  	// rootFound is called before scanning a new root dir. If it returns true,
  1020  	// the root will be scanned. Returning false will not necessarily prevent
  1021  	// directories from that root making it to dirFound.
  1022  	rootFound func(gopathwalk.Root) bool
  1023  	// dirFound is called when a directory is found that is possibly a Go package.
  1024  	// pkg will be populated with everything except packageName.
  1025  	// If it returns true, the package's name will be loaded.
  1026  	dirFound func(pkg *pkg) bool
  1027  	// packageNameLoaded is called when a package is found and its name is loaded.
  1028  	// If it returns true, the package's exports will be loaded.
  1029  	packageNameLoaded func(pkg *pkg) bool
  1030  	// exportsLoaded is called when a package's exports have been loaded.
  1031  	exportsLoaded func(pkg *pkg, exports []string)
  1032  }
  1033  
  1034  func addExternalCandidates(pass *pass, refs references, filename string) error {
  1035  	var mu sync.Mutex
  1036  	found := make(map[string][]pkgDistance)
  1037  	callback := &scanCallback{
  1038  		rootFound: func(gopathwalk.Root) bool {
  1039  			return true // We want everything.
  1040  		},
  1041  		dirFound: func(pkg *pkg) bool {
  1042  			return pkgIsCandidate(filename, refs, pkg)
  1043  		},
  1044  		packageNameLoaded: func(pkg *pkg) bool {
  1045  			if _, want := refs[pkg.packageName]; !want {
  1046  				return false
  1047  			}
  1048  			if pkg.dir == pass.srcDir && pass.f.Name.Name == pkg.packageName {
  1049  				// The candidate is in the same directory and has the
  1050  				// same package name. Don't try to import ourselves.
  1051  				return false
  1052  			}
  1053  			if !canUse(filename, pkg.dir) {
  1054  				return false
  1055  			}
  1056  			mu.Lock()
  1057  			defer mu.Unlock()
  1058  			found[pkg.packageName] = append(found[pkg.packageName], pkgDistance{pkg, distance(pass.srcDir, pkg.dir)})
  1059  			return false // We'll do our own loading after we sort.
  1060  		},
  1061  	}
  1062  	resolver, err := pass.env.GetResolver()
  1063  	if err != nil {
  1064  		return err
  1065  	}
  1066  	if err = resolver.scan(context.Background(), callback); err != nil {
  1067  		return err
  1068  	}
  1069  
  1070  	// Search for imports matching potential package references.
  1071  	type result struct {
  1072  		imp *ImportInfo
  1073  		pkg *packageInfo
  1074  	}
  1075  	results := make(chan result, len(refs))
  1076  
  1077  	ctx, cancel := context.WithCancel(context.TODO())
  1078  	var wg sync.WaitGroup
  1079  	defer func() {
  1080  		cancel()
  1081  		wg.Wait()
  1082  	}()
  1083  	var (
  1084  		firstErr     error
  1085  		firstErrOnce sync.Once
  1086  	)
  1087  	for pkgName, symbols := range refs {
  1088  		wg.Add(1)
  1089  		go func(pkgName string, symbols map[string]bool) {
  1090  			defer wg.Done()
  1091  
  1092  			found, err := findImport(ctx, pass, found[pkgName], pkgName, symbols, filename)
  1093  
  1094  			if err != nil {
  1095  				firstErrOnce.Do(func() {
  1096  					firstErr = err
  1097  					cancel()
  1098  				})
  1099  				return
  1100  			}
  1101  
  1102  			if found == nil {
  1103  				return // No matching package.
  1104  			}
  1105  
  1106  			imp := &ImportInfo{
  1107  				ImportPath: found.importPathShort,
  1108  			}
  1109  
  1110  			pkg := &packageInfo{
  1111  				name:    pkgName,
  1112  				exports: symbols,
  1113  			}
  1114  			results <- result{imp, pkg}
  1115  		}(pkgName, symbols)
  1116  	}
  1117  	go func() {
  1118  		wg.Wait()
  1119  		close(results)
  1120  	}()
  1121  
  1122  	for result := range results {
  1123  		pass.addCandidate(result.imp, result.pkg)
  1124  	}
  1125  	return firstErr
  1126  }
  1127  
  1128  // notIdentifier reports whether ch is an invalid identifier character.
  1129  func notIdentifier(ch rune) bool {
  1130  	return !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' ||
  1131  		'0' <= ch && ch <= '9' ||
  1132  		ch == '_' ||
  1133  		ch >= utf8.RuneSelf && (unicode.IsLetter(ch) || unicode.IsDigit(ch)))
  1134  }
  1135  
  1136  // ImportPathToAssumedName returns the assumed package name of an import path.
  1137  // It does this using only string parsing of the import path.
  1138  // It picks the last element of the path that does not look like a major
  1139  // version, and then picks the valid identifier off the start of that element.
  1140  // It is used to determine if a local rename should be added to an import for
  1141  // clarity.
  1142  // This function could be moved to a standard package and exported if we want
  1143  // for use in other tools.
  1144  func ImportPathToAssumedName(importPath string) string {
  1145  	base := path.Base(importPath)
  1146  	if strings.HasPrefix(base, "v") {
  1147  		if _, err := strconv.Atoi(base[1:]); err == nil {
  1148  			dir := path.Dir(importPath)
  1149  			if dir != "." {
  1150  				base = path.Base(dir)
  1151  			}
  1152  		}
  1153  	}
  1154  	base = strings.TrimPrefix(base, "go-")
  1155  	if i := strings.IndexFunc(base, notIdentifier); i >= 0 {
  1156  		base = base[:i]
  1157  	}
  1158  	return base
  1159  }
  1160  
  1161  // gopathResolver implements resolver for GOPATH workspaces.
  1162  type gopathResolver struct {
  1163  	env      *ProcessEnv
  1164  	walked   bool
  1165  	cache    *dirInfoCache
  1166  	scanSema chan struct{} // scanSema prevents concurrent scans.
  1167  }
  1168  
  1169  func newGopathResolver(env *ProcessEnv) *gopathResolver {
  1170  	r := &gopathResolver{
  1171  		env: env,
  1172  		cache: &dirInfoCache{
  1173  			dirs:      map[string]*directoryPackageInfo{},
  1174  			listeners: map[*int]cacheListener{},
  1175  		},
  1176  		scanSema: make(chan struct{}, 1),
  1177  	}
  1178  	r.scanSema <- struct{}{}
  1179  	return r
  1180  }
  1181  
  1182  func (r *gopathResolver) ClearForNewScan() {
  1183  	<-r.scanSema
  1184  	r.cache = &dirInfoCache{
  1185  		dirs:      map[string]*directoryPackageInfo{},
  1186  		listeners: map[*int]cacheListener{},
  1187  	}
  1188  	r.walked = false
  1189  	r.scanSema <- struct{}{}
  1190  }
  1191  
  1192  func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
  1193  	names := map[string]string{}
  1194  	bctx, err := r.env.buildContext()
  1195  	if err != nil {
  1196  		return nil, err
  1197  	}
  1198  	for _, path := range importPaths {
  1199  		names[path] = importPathToName(bctx, path, srcDir)
  1200  	}
  1201  	return names, nil
  1202  }
  1203  
  1204  // importPathToName finds out the actual package name, as declared in its .go files.
  1205  func importPathToName(bctx *build.Context, importPath, srcDir string) string {
  1206  	// Fast path for standard library without going to disk.
  1207  	if _, ok := stdlib[importPath]; ok {
  1208  		return path.Base(importPath) // stdlib packages always match their paths.
  1209  	}
  1210  
  1211  	buildPkg, err := bctx.Import(importPath, srcDir, build.FindOnly)
  1212  	if err != nil {
  1213  		return ""
  1214  	}
  1215  	pkgName, err := packageDirToName(buildPkg.Dir)
  1216  	if err != nil {
  1217  		return ""
  1218  	}
  1219  	return pkgName
  1220  }
  1221  
  1222  // packageDirToName is a faster version of build.Import if
  1223  // the only thing desired is the package name. Given a directory,
  1224  // packageDirToName then only parses one file in the package,
  1225  // trusting that the files in the directory are consistent.
  1226  func packageDirToName(dir string) (packageName string, err error) {
  1227  	d, err := os.Open(dir)
  1228  	if err != nil {
  1229  		return "", err
  1230  	}
  1231  	names, err := d.Readdirnames(-1)
  1232  	d.Close()
  1233  	if err != nil {
  1234  		return "", err
  1235  	}
  1236  	sort.Strings(names) // to have predictable behavior
  1237  	var lastErr error
  1238  	var nfile int
  1239  	for _, name := range names {
  1240  		if !strings.HasSuffix(name, ".go") {
  1241  			continue
  1242  		}
  1243  		if strings.HasSuffix(name, "_test.go") {
  1244  			continue
  1245  		}
  1246  		nfile++
  1247  		fullFile := filepath.Join(dir, name)
  1248  
  1249  		fset := token.NewFileSet()
  1250  		f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly)
  1251  		if err != nil {
  1252  			lastErr = err
  1253  			continue
  1254  		}
  1255  		pkgName := f.Name.Name
  1256  		if pkgName == "documentation" {
  1257  			// Special case from go/build.ImportDir, not
  1258  			// handled by ctx.MatchFile.
  1259  			continue
  1260  		}
  1261  		if pkgName == "main" {
  1262  			// Also skip package main, assuming it's a +build ignore generator or example.
  1263  			// Since you can't import a package main anyway, there's no harm here.
  1264  			continue
  1265  		}
  1266  		return pkgName, nil
  1267  	}
  1268  	if lastErr != nil {
  1269  		return "", lastErr
  1270  	}
  1271  	return "", fmt.Errorf("no importable package found in %d Go files", nfile)
  1272  }
  1273  
  1274  type pkg struct {
  1275  	dir             string  // absolute file path to pkg directory ("/usr/lib/go/src/net/http")
  1276  	importPathShort string  // vendorless import path ("net/http", "a/b")
  1277  	packageName     string  // package name loaded from source if requested
  1278  	relevance       float64 // a weakly-defined score of how relevant a package is. 0 is most relevant.
  1279  }
  1280  
  1281  type pkgDistance struct {
  1282  	pkg      *pkg
  1283  	distance int // relative distance to target
  1284  }
  1285  
  1286  // byDistanceOrImportPathShortLength sorts by relative distance breaking ties
  1287  // on the short import path length and then the import string itself.
  1288  type byDistanceOrImportPathShortLength []pkgDistance
  1289  
  1290  func (s byDistanceOrImportPathShortLength) Len() int { return len(s) }
  1291  func (s byDistanceOrImportPathShortLength) Less(i, j int) bool {
  1292  	di, dj := s[i].distance, s[j].distance
  1293  	if di == -1 {
  1294  		return false
  1295  	}
  1296  	if dj == -1 {
  1297  		return true
  1298  	}
  1299  	if di != dj {
  1300  		return di < dj
  1301  	}
  1302  
  1303  	vi, vj := s[i].pkg.importPathShort, s[j].pkg.importPathShort
  1304  	if len(vi) != len(vj) {
  1305  		return len(vi) < len(vj)
  1306  	}
  1307  	return vi < vj
  1308  }
  1309  func (s byDistanceOrImportPathShortLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  1310  
  1311  func distance(basepath, targetpath string) int {
  1312  	p, err := filepath.Rel(basepath, targetpath)
  1313  	if err != nil {
  1314  		return -1
  1315  	}
  1316  	if p == "." {
  1317  		return 0
  1318  	}
  1319  	return strings.Count(p, string(filepath.Separator)) + 1
  1320  }
  1321  
  1322  func (r *gopathResolver) scan(ctx context.Context, callback *scanCallback) error {
  1323  	add := func(root gopathwalk.Root, dir string) {
  1324  		// We assume cached directories have not changed. We can skip them and their
  1325  		// children.
  1326  		if _, ok := r.cache.Load(dir); ok {
  1327  			return
  1328  		}
  1329  
  1330  		importpath := filepath.ToSlash(dir[len(root.Path)+len("/"):])
  1331  		info := directoryPackageInfo{
  1332  			status:                 directoryScanned,
  1333  			dir:                    dir,
  1334  			rootType:               root.Type,
  1335  			nonCanonicalImportPath: VendorlessPath(importpath),
  1336  		}
  1337  		r.cache.Store(dir, info)
  1338  	}
  1339  	processDir := func(info directoryPackageInfo) {
  1340  		// Skip this directory if we were not able to get the package information successfully.
  1341  		if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil {
  1342  			return
  1343  		}
  1344  
  1345  		p := &pkg{
  1346  			importPathShort: info.nonCanonicalImportPath,
  1347  			dir:             info.dir,
  1348  			relevance:       MaxRelevance - 1,
  1349  		}
  1350  		if info.rootType == gopathwalk.RootGOROOT {
  1351  			p.relevance = MaxRelevance
  1352  		}
  1353  
  1354  		if !callback.dirFound(p) {
  1355  			return
  1356  		}
  1357  		var err error
  1358  		p.packageName, err = r.cache.CachePackageName(info)
  1359  		if err != nil {
  1360  			return
  1361  		}
  1362  
  1363  		if !callback.packageNameLoaded(p) {
  1364  			return
  1365  		}
  1366  		if _, exports, err := r.loadExports(ctx, p, false); err == nil {
  1367  			callback.exportsLoaded(p, exports)
  1368  		}
  1369  	}
  1370  	stop := r.cache.ScanAndListen(ctx, processDir)
  1371  	defer stop()
  1372  
  1373  	goenv, err := r.env.goEnv()
  1374  	if err != nil {
  1375  		return err
  1376  	}
  1377  	var roots []gopathwalk.Root
  1378  	roots = append(roots, gopathwalk.Root{Path: filepath.Join(goenv["GOROOT"], "src"), Type: gopathwalk.RootGOROOT})
  1379  	for _, p := range filepath.SplitList(goenv["GOPATH"]) {
  1380  		roots = append(roots, gopathwalk.Root{Path: filepath.Join(p, "src"), Type: gopathwalk.RootGOPATH})
  1381  	}
  1382  	// The callback is not necessarily safe to use in the goroutine below. Process roots eagerly.
  1383  	roots = filterRoots(roots, callback.rootFound)
  1384  	// We can't cancel walks, because we need them to finish to have a usable
  1385  	// cache. Instead, run them in a separate goroutine and detach.
  1386  	scanDone := make(chan struct{})
  1387  	go func() {
  1388  		select {
  1389  		case <-ctx.Done():
  1390  			return
  1391  		case <-r.scanSema:
  1392  		}
  1393  		defer func() { r.scanSema <- struct{}{} }()
  1394  		gopathwalk.Walk(roots, add, gopathwalk.Options{Logf: r.env.Logf, ModulesEnabled: false})
  1395  		close(scanDone)
  1396  	}()
  1397  	select {
  1398  	case <-ctx.Done():
  1399  	case <-scanDone:
  1400  	}
  1401  	return nil
  1402  }
  1403  
  1404  func (r *gopathResolver) scoreImportPath(ctx context.Context, path string) float64 {
  1405  	if _, ok := stdlib[path]; ok {
  1406  		return MaxRelevance
  1407  	}
  1408  	return MaxRelevance - 1
  1409  }
  1410  
  1411  func filterRoots(roots []gopathwalk.Root, include func(gopathwalk.Root) bool) []gopathwalk.Root {
  1412  	var result []gopathwalk.Root
  1413  	for _, root := range roots {
  1414  		if !include(root) {
  1415  			continue
  1416  		}
  1417  		result = append(result, root)
  1418  	}
  1419  	return result
  1420  }
  1421  
  1422  func (r *gopathResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) {
  1423  	if info, ok := r.cache.Load(pkg.dir); ok && !includeTest {
  1424  		return r.cache.CacheExports(ctx, r.env, info)
  1425  	}
  1426  	return loadExportsFromFiles(ctx, r.env, pkg.dir, includeTest)
  1427  }
  1428  
  1429  // VendorlessPath returns the devendorized version of the import path ipath.
  1430  // For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b".
  1431  func VendorlessPath(ipath string) string {
  1432  	// Devendorize for use in import statement.
  1433  	if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 {
  1434  		return ipath[i+len("/vendor/"):]
  1435  	}
  1436  	if strings.HasPrefix(ipath, "vendor/") {
  1437  		return ipath[len("vendor/"):]
  1438  	}
  1439  	return ipath
  1440  }
  1441  
  1442  func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, includeTest bool) (string, []string, error) {
  1443  	// Look for non-test, buildable .go files which could provide exports.
  1444  	all, err := ioutil.ReadDir(dir)
  1445  	if err != nil {
  1446  		return "", nil, err
  1447  	}
  1448  	var files []os.FileInfo
  1449  	for _, fi := range all {
  1450  		name := fi.Name()
  1451  		if !strings.HasSuffix(name, ".go") || (!includeTest && strings.HasSuffix(name, "_test.go")) {
  1452  			continue
  1453  		}
  1454  		match, err := env.matchFile(dir, fi.Name())
  1455  		if err != nil || !match {
  1456  			continue
  1457  		}
  1458  		files = append(files, fi)
  1459  	}
  1460  
  1461  	if len(files) == 0 {
  1462  		return "", nil, fmt.Errorf("dir %v contains no buildable, non-test .go files", dir)
  1463  	}
  1464  
  1465  	var pkgName string
  1466  	var exports []string
  1467  	fset := token.NewFileSet()
  1468  	for _, fi := range files {
  1469  		select {
  1470  		case <-ctx.Done():
  1471  			return "", nil, ctx.Err()
  1472  		default:
  1473  		}
  1474  
  1475  		fullFile := filepath.Join(dir, fi.Name())
  1476  		f, err := parser.ParseFile(fset, fullFile, nil, 0)
  1477  		if err != nil {
  1478  			if env.Logf != nil {
  1479  				env.Logf("error parsing %v: %v", fullFile, err)
  1480  			}
  1481  			continue
  1482  		}
  1483  		if f.Name.Name == "documentation" {
  1484  			// Special case from go/build.ImportDir, not
  1485  			// handled by MatchFile above.
  1486  			continue
  1487  		}
  1488  		if includeTest && strings.HasSuffix(f.Name.Name, "_test") {
  1489  			// x_test package. We want internal test files only.
  1490  			continue
  1491  		}
  1492  		pkgName = f.Name.Name
  1493  		for name := range f.Scope.Objects {
  1494  			if ast.IsExported(name) {
  1495  				exports = append(exports, name)
  1496  			}
  1497  		}
  1498  	}
  1499  
  1500  	if env.Logf != nil {
  1501  		sortedExports := append([]string(nil), exports...)
  1502  		sort.Strings(sortedExports)
  1503  		env.Logf("loaded exports in dir %v (package %v): %v", dir, pkgName, strings.Join(sortedExports, ", "))
  1504  	}
  1505  	return pkgName, exports, nil
  1506  }
  1507  
  1508  // findImport searches for a package with the given symbols.
  1509  // If no package is found, findImport returns ("", false, nil)
  1510  func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgName string, symbols map[string]bool, filename string) (*pkg, error) {
  1511  	// Sort the candidates by their import package length,
  1512  	// assuming that shorter package names are better than long
  1513  	// ones.  Note that this sorts by the de-vendored name, so
  1514  	// there's no "penalty" for vendoring.
  1515  	sort.Sort(byDistanceOrImportPathShortLength(candidates))
  1516  	if pass.env.Logf != nil {
  1517  		for i, c := range candidates {
  1518  			pass.env.Logf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir)
  1519  		}
  1520  	}
  1521  	resolver, err := pass.env.GetResolver()
  1522  	if err != nil {
  1523  		return nil, err
  1524  	}
  1525  
  1526  	// Collect exports for packages with matching names.
  1527  	rescv := make([]chan *pkg, len(candidates))
  1528  	for i := range candidates {
  1529  		rescv[i] = make(chan *pkg, 1)
  1530  	}
  1531  	const maxConcurrentPackageImport = 4
  1532  	loadExportsSem := make(chan struct{}, maxConcurrentPackageImport)
  1533  
  1534  	ctx, cancel := context.WithCancel(ctx)
  1535  	var wg sync.WaitGroup
  1536  	defer func() {
  1537  		cancel()
  1538  		wg.Wait()
  1539  	}()
  1540  
  1541  	wg.Add(1)
  1542  	go func() {
  1543  		defer wg.Done()
  1544  		for i, c := range candidates {
  1545  			select {
  1546  			case loadExportsSem <- struct{}{}:
  1547  			case <-ctx.Done():
  1548  				return
  1549  			}
  1550  
  1551  			wg.Add(1)
  1552  			go func(c pkgDistance, resc chan<- *pkg) {
  1553  				defer func() {
  1554  					<-loadExportsSem
  1555  					wg.Done()
  1556  				}()
  1557  
  1558  				if pass.env.Logf != nil {
  1559  					pass.env.Logf("loading exports in dir %s (seeking package %s)", c.pkg.dir, pkgName)
  1560  				}
  1561  				// If we're an x_test, load the package under test's test variant.
  1562  				includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir
  1563  				_, exports, err := resolver.loadExports(ctx, c.pkg, includeTest)
  1564  				if err != nil {
  1565  					if pass.env.Logf != nil {
  1566  						pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
  1567  					}
  1568  					resc <- nil
  1569  					return
  1570  				}
  1571  
  1572  				exportsMap := make(map[string]bool, len(exports))
  1573  				for _, sym := range exports {
  1574  					exportsMap[sym] = true
  1575  				}
  1576  
  1577  				// If it doesn't have the right
  1578  				// symbols, send nil to mean no match.
  1579  				for symbol := range symbols {
  1580  					if !exportsMap[symbol] {
  1581  						resc <- nil
  1582  						return
  1583  					}
  1584  				}
  1585  				resc <- c.pkg
  1586  			}(c, rescv[i])
  1587  		}
  1588  	}()
  1589  
  1590  	for _, resc := range rescv {
  1591  		pkg := <-resc
  1592  		if pkg == nil {
  1593  			continue
  1594  		}
  1595  		return pkg, nil
  1596  	}
  1597  	return nil, nil
  1598  }
  1599  
  1600  // pkgIsCandidate reports whether pkg is a candidate for satisfying the
  1601  // finding which package pkgIdent in the file named by filename is trying
  1602  // to refer to.
  1603  //
  1604  // This check is purely lexical and is meant to be as fast as possible
  1605  // because it's run over all $GOPATH directories to filter out poor
  1606  // candidates in order to limit the CPU and I/O later parsing the
  1607  // exports in candidate packages.
  1608  //
  1609  // filename is the file being formatted.
  1610  // pkgIdent is the package being searched for, like "client" (if
  1611  // searching for "client.New")
  1612  func pkgIsCandidate(filename string, refs references, pkg *pkg) bool {
  1613  	// Check "internal" and "vendor" visibility:
  1614  	if !canUse(filename, pkg.dir) {
  1615  		return false
  1616  	}
  1617  
  1618  	// Speed optimization to minimize disk I/O:
  1619  	// the last two components on disk must contain the
  1620  	// package name somewhere.
  1621  	//
  1622  	// This permits mismatch naming like directory
  1623  	// "go-foo" being package "foo", or "pkg.v3" being "pkg",
  1624  	// or directory "google.golang.org/api/cloudbilling/v1"
  1625  	// being package "cloudbilling", but doesn't
  1626  	// permit a directory "foo" to be package
  1627  	// "bar", which is strongly discouraged
  1628  	// anyway. There's no reason goimports needs
  1629  	// to be slow just to accommodate that.
  1630  	for pkgIdent := range refs {
  1631  		lastTwo := lastTwoComponents(pkg.importPathShort)
  1632  		if strings.Contains(lastTwo, pkgIdent) {
  1633  			return true
  1634  		}
  1635  		if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(pkgIdent) {
  1636  			lastTwo = lowerASCIIAndRemoveHyphen(lastTwo)
  1637  			if strings.Contains(lastTwo, pkgIdent) {
  1638  				return true
  1639  			}
  1640  		}
  1641  	}
  1642  	return false
  1643  }
  1644  
  1645  func hasHyphenOrUpperASCII(s string) bool {
  1646  	for i := 0; i < len(s); i++ {
  1647  		b := s[i]
  1648  		if b == '-' || ('A' <= b && b <= 'Z') {
  1649  			return true
  1650  		}
  1651  	}
  1652  	return false
  1653  }
  1654  
  1655  func lowerASCIIAndRemoveHyphen(s string) (ret string) {
  1656  	buf := make([]byte, 0, len(s))
  1657  	for i := 0; i < len(s); i++ {
  1658  		b := s[i]
  1659  		switch {
  1660  		case b == '-':
  1661  			continue
  1662  		case 'A' <= b && b <= 'Z':
  1663  			buf = append(buf, b+('a'-'A'))
  1664  		default:
  1665  			buf = append(buf, b)
  1666  		}
  1667  	}
  1668  	return string(buf)
  1669  }
  1670  
  1671  // canUse reports whether the package in dir is usable from filename,
  1672  // respecting the Go "internal" and "vendor" visibility rules.
  1673  func canUse(filename, dir string) bool {
  1674  	// Fast path check, before any allocations. If it doesn't contain vendor
  1675  	// or internal, it's not tricky:
  1676  	// Note that this can false-negative on directories like "notinternal",
  1677  	// but we check it correctly below. This is just a fast path.
  1678  	if !strings.Contains(dir, "vendor") && !strings.Contains(dir, "internal") {
  1679  		return true
  1680  	}
  1681  
  1682  	dirSlash := filepath.ToSlash(dir)
  1683  	if !strings.Contains(dirSlash, "/vendor/") && !strings.Contains(dirSlash, "/internal/") && !strings.HasSuffix(dirSlash, "/internal") {
  1684  		return true
  1685  	}
  1686  	// Vendor or internal directory only visible from children of parent.
  1687  	// That means the path from the current directory to the target directory
  1688  	// can contain ../vendor or ../internal but not ../foo/vendor or ../foo/internal
  1689  	// or bar/vendor or bar/internal.
  1690  	// After stripping all the leading ../, the only okay place to see vendor or internal
  1691  	// is at the very beginning of the path.
  1692  	absfile, err := filepath.Abs(filename)
  1693  	if err != nil {
  1694  		return false
  1695  	}
  1696  	absdir, err := filepath.Abs(dir)
  1697  	if err != nil {
  1698  		return false
  1699  	}
  1700  	rel, err := filepath.Rel(absfile, absdir)
  1701  	if err != nil {
  1702  		return false
  1703  	}
  1704  	relSlash := filepath.ToSlash(rel)
  1705  	if i := strings.LastIndex(relSlash, "../"); i >= 0 {
  1706  		relSlash = relSlash[i+len("../"):]
  1707  	}
  1708  	return !strings.Contains(relSlash, "/vendor/") && !strings.Contains(relSlash, "/internal/") && !strings.HasSuffix(relSlash, "/internal")
  1709  }
  1710  
  1711  // lastTwoComponents returns at most the last two path components
  1712  // of v, using either / or \ as the path separator.
  1713  func lastTwoComponents(v string) string {
  1714  	nslash := 0
  1715  	for i := len(v) - 1; i >= 0; i-- {
  1716  		if v[i] == '/' || v[i] == '\\' {
  1717  			nslash++
  1718  			if nslash == 2 {
  1719  				return v[i:]
  1720  			}
  1721  		}
  1722  	}
  1723  	return v
  1724  }
  1725  
  1726  type visitFn func(node ast.Node) ast.Visitor
  1727  
  1728  func (fn visitFn) Visit(node ast.Node) ast.Visitor {
  1729  	return fn(node)
  1730  }
  1731  
  1732  func copyExports(pkg []string) map[string]bool {
  1733  	m := make(map[string]bool, len(pkg))
  1734  	for _, v := range pkg {
  1735  		m[v] = true
  1736  	}
  1737  	return m
  1738  }