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