src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

fix.go (58080B)


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