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