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