mirror of
https://github.com/golang/go
synced 2024-11-18 17:04:41 -07:00
internal/imports: cache GOPATH, exports
We intend to use the GOPATH resolver's results during LSP autocompletion. That means we have to be able to cache its data, same as we do for modules. Convert it to use a dirInfoCache. Cache exports in the dirInfoCache. Along the way, store exports as slices rather than maps. We don't need the extra structure the vast majority of the time, and the memory overhead is nontrivial. Change-Id: If267d6b00da2163a960b93b2cf2088ec2538f73d Reviewed-on: https://go-review.googlesource.com/c/tools/+/205162 Run-TryBot: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
f505e54dbd
commit
c02c21e5e9
@ -683,11 +683,11 @@ func getPackageExports(completePackage, filename string, env *ProcessEnv) ([]Pac
|
|||||||
IdentName: pkg.packageName,
|
IdentName: pkg.packageName,
|
||||||
FixType: AddImport,
|
FixType: AddImport,
|
||||||
}
|
}
|
||||||
var exportsMap map[string]bool
|
var exports []string
|
||||||
if e, ok := stdlib[pkg.importPathShort]; ok {
|
if e, ok := stdlib[pkg.importPathShort]; ok {
|
||||||
exportsMap = e
|
exports = e
|
||||||
} else {
|
} else {
|
||||||
exportsMap, err = env.GetResolver().loadExports(context.TODO(), completePackage, pkg)
|
exports, err = loadExportsForPackage(context.Background(), env, completePackage, pkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if env.Debug {
|
if env.Debug {
|
||||||
env.Logf("while completing %q, error loading exports from %q: %v", completePackage, pkg.importPathShort, err)
|
env.Logf("while completing %q, error loading exports from %q: %v", completePackage, pkg.importPathShort, err)
|
||||||
@ -695,10 +695,6 @@ func getPackageExports(completePackage, filename string, env *ProcessEnv) ([]Pac
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var exports []string
|
|
||||||
for export := range exportsMap {
|
|
||||||
exports = append(exports, export)
|
|
||||||
}
|
|
||||||
sort.Strings(exports)
|
sort.Strings(exports)
|
||||||
results = append(results, PackageExport{
|
results = append(results, PackageExport{
|
||||||
Fix: fix,
|
Fix: fix,
|
||||||
@ -848,9 +844,8 @@ type Resolver interface {
|
|||||||
// could not be determined will be excluded.
|
// could not be determined will be excluded.
|
||||||
scan(refs references, loadNames bool, exclude []gopathwalk.RootType) ([]*pkg, error)
|
scan(refs references, loadNames bool, exclude []gopathwalk.RootType) ([]*pkg, error)
|
||||||
// loadExports returns the set of exported symbols in the package at dir.
|
// loadExports returns the set of exported symbols in the package at dir.
|
||||||
// It returns an error if the package name in dir does not match expectPackage.
|
|
||||||
// loadExports may be called concurrently.
|
// loadExports may be called concurrently.
|
||||||
loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error)
|
loadExports(ctx context.Context, pkg *pkg) (string, []string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gopackagesResolver implements resolver for GOPATH and module workspaces using go/packages.
|
// gopackagesResolver implements resolver for GOPATH and module workspaces using go/packages.
|
||||||
@ -906,24 +901,24 @@ func (r *goPackagesResolver) scan(refs references, _ bool, _ []gopathwalk.RootTy
|
|||||||
return scan, nil
|
return scan, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *goPackagesResolver) loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error) {
|
func (r *goPackagesResolver) loadExports(ctx context.Context, pkg *pkg) (string, []string, error) {
|
||||||
if pkg.goPackage == nil {
|
if pkg.goPackage == nil {
|
||||||
return nil, fmt.Errorf("goPackage not set")
|
return "", nil, fmt.Errorf("goPackage not set")
|
||||||
}
|
}
|
||||||
exports := map[string]bool{}
|
var exports []string
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
for _, fname := range pkg.goPackage.CompiledGoFiles {
|
for _, fname := range pkg.goPackage.CompiledGoFiles {
|
||||||
f, err := parser.ParseFile(fset, fname, nil, 0)
|
f, err := parser.ParseFile(fset, fname, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing %s: %v", fname, err)
|
return "", nil, fmt.Errorf("parsing %s: %v", fname, err)
|
||||||
}
|
}
|
||||||
for name := range f.Scope.Objects {
|
for name := range f.Scope.Objects {
|
||||||
if ast.IsExported(name) {
|
if ast.IsExported(name) {
|
||||||
exports[name] = true
|
exports = append(exports, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return exports, nil
|
return pkg.goPackage.Name, exports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addExternalCandidates(pass *pass, refs references, filename string) error {
|
func addExternalCandidates(pass *pass, refs references, filename string) error {
|
||||||
@ -1025,10 +1020,20 @@ func importPathToAssumedName(importPath string) string {
|
|||||||
|
|
||||||
// gopathResolver implements resolver for GOPATH workspaces.
|
// gopathResolver implements resolver for GOPATH workspaces.
|
||||||
type gopathResolver struct {
|
type gopathResolver struct {
|
||||||
env *ProcessEnv
|
env *ProcessEnv
|
||||||
|
cache *dirInfoCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *gopathResolver) init() {
|
||||||
|
if r.cache == nil {
|
||||||
|
r.cache = &dirInfoCache{
|
||||||
|
dirs: map[string]*directoryPackageInfo{},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
||||||
|
r.init()
|
||||||
names := map[string]string{}
|
names := map[string]string{}
|
||||||
for _, path := range importPaths {
|
for _, path := range importPaths {
|
||||||
names[path] = importPathToName(r.env, path, srcDir)
|
names[path] = importPathToName(r.env, path, srcDir)
|
||||||
@ -1157,39 +1162,50 @@ func distance(basepath, targetpath string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *gopathResolver) scan(_ references, loadNames bool, exclude []gopathwalk.RootType) ([]*pkg, error) {
|
func (r *gopathResolver) scan(_ references, loadNames bool, exclude []gopathwalk.RootType) ([]*pkg, error) {
|
||||||
dupCheck := make(map[string]bool)
|
r.init()
|
||||||
var result []*pkg
|
|
||||||
|
|
||||||
var mu sync.Mutex
|
|
||||||
|
|
||||||
add := func(root gopathwalk.Root, dir string) {
|
add := func(root gopathwalk.Root, dir string) {
|
||||||
mu.Lock()
|
// We assume cached directories have not changed. We can skip them and their
|
||||||
defer mu.Unlock()
|
// children.
|
||||||
|
if _, ok := r.cache.Load(dir); ok {
|
||||||
if _, dup := dupCheck[dir]; dup {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dupCheck[dir] = true
|
|
||||||
importpath := filepath.ToSlash(dir[len(root.Path)+len("/"):])
|
importpath := filepath.ToSlash(dir[len(root.Path)+len("/"):])
|
||||||
p := &pkg{
|
info := directoryPackageInfo{
|
||||||
importPathShort: VendorlessPath(importpath),
|
status: directoryScanned,
|
||||||
dir: dir,
|
dir: dir,
|
||||||
relevance: 1,
|
rootType: root.Type,
|
||||||
|
nonCanonicalImportPath: VendorlessPath(importpath),
|
||||||
}
|
}
|
||||||
if root.Type == gopathwalk.RootGOROOT {
|
r.cache.Store(dir, info)
|
||||||
p.relevance = 0
|
|
||||||
}
|
|
||||||
if loadNames {
|
|
||||||
var err error
|
|
||||||
p.packageName, err = packageDirToName(dir)
|
|
||||||
if err != nil {
|
|
||||||
return // Typically an unimportable package like main.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = append(result, p)
|
|
||||||
}
|
}
|
||||||
roots := filterRoots(gopathwalk.SrcDirsRoots(r.env.buildContext()), exclude)
|
roots := filterRoots(gopathwalk.SrcDirsRoots(r.env.buildContext()), exclude)
|
||||||
gopathwalk.Walk(roots, add, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: false})
|
gopathwalk.Walk(roots, add, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: false})
|
||||||
|
var result []*pkg
|
||||||
|
for _, dir := range r.cache.Keys() {
|
||||||
|
info, ok := r.cache.Load(dir)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if loadNames {
|
||||||
|
var err error
|
||||||
|
info, err = r.cache.CachePackageName(info)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p := &pkg{
|
||||||
|
importPathShort: info.nonCanonicalImportPath,
|
||||||
|
dir: dir,
|
||||||
|
relevance: 1,
|
||||||
|
packageName: info.packageName,
|
||||||
|
}
|
||||||
|
if info.rootType == gopathwalk.RootGOROOT {
|
||||||
|
p.relevance = 0
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1207,8 +1223,12 @@ outer:
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *gopathResolver) loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error) {
|
func (r *gopathResolver) loadExports(ctx context.Context, pkg *pkg) (string, []string, error) {
|
||||||
return loadExportsFromFiles(ctx, r.env, expectPackage, pkg.dir)
|
r.init()
|
||||||
|
if info, ok := r.cache.Load(pkg.dir); ok {
|
||||||
|
return r.cache.CacheExports(ctx, r.env, info)
|
||||||
|
}
|
||||||
|
return loadExportsFromFiles(ctx, r.env, pkg.dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VendorlessPath returns the devendorized version of the import path ipath.
|
// VendorlessPath returns the devendorized version of the import path ipath.
|
||||||
@ -1224,13 +1244,13 @@ func VendorlessPath(ipath string) string {
|
|||||||
return ipath
|
return ipath
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, expectPackage string, dir string) (map[string]bool, error) {
|
func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string) (string, []string, error) {
|
||||||
exports := make(map[string]bool)
|
var exports []string
|
||||||
|
|
||||||
// Look for non-test, buildable .go files which could provide exports.
|
// Look for non-test, buildable .go files which could provide exports.
|
||||||
all, err := ioutil.ReadDir(dir)
|
all, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
var files []os.FileInfo
|
var files []os.FileInfo
|
||||||
for _, fi := range all {
|
for _, fi := range all {
|
||||||
@ -1246,47 +1266,42 @@ func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, expectPackage st
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(files) == 0 {
|
if len(files) == 0 {
|
||||||
return nil, fmt.Errorf("dir %v contains no buildable, non-test .go files", dir)
|
return "", nil, fmt.Errorf("dir %v contains no buildable, non-test .go files", dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pkgName string
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
for _, fi := range files {
|
for _, fi := range files {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return "", nil, ctx.Err()
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
fullFile := filepath.Join(dir, fi.Name())
|
fullFile := filepath.Join(dir, fi.Name())
|
||||||
f, err := parser.ParseFile(fset, fullFile, nil, 0)
|
f, err := parser.ParseFile(fset, fullFile, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing %s: %v", fullFile, err)
|
return "", nil, fmt.Errorf("parsing %s: %v", fullFile, err)
|
||||||
}
|
}
|
||||||
pkgName := f.Name.Name
|
if f.Name.Name == "documentation" {
|
||||||
if pkgName == "documentation" {
|
|
||||||
// Special case from go/build.ImportDir, not
|
// Special case from go/build.ImportDir, not
|
||||||
// handled by MatchFile above.
|
// handled by MatchFile above.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if pkgName != expectPackage {
|
pkgName = f.Name.Name
|
||||||
return nil, fmt.Errorf("scan of dir %v is not expected package %v (actually %v)", dir, expectPackage, pkgName)
|
|
||||||
}
|
|
||||||
for name := range f.Scope.Objects {
|
for name := range f.Scope.Objects {
|
||||||
if ast.IsExported(name) {
|
if ast.IsExported(name) {
|
||||||
exports[name] = true
|
exports = append(exports, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if env.Debug {
|
if env.Debug {
|
||||||
exportList := make([]string, 0, len(exports))
|
sortedExports := append([]string(nil), exports...)
|
||||||
for k := range exports {
|
sort.Strings(sortedExports)
|
||||||
exportList = append(exportList, k)
|
env.Logf("loaded exports in dir %v (package %v): %v", dir, pkgName, strings.Join(sortedExports, ", "))
|
||||||
}
|
|
||||||
sort.Strings(exportList)
|
|
||||||
env.Logf("loaded exports in dir %v (package %v): %v", dir, expectPackage, strings.Join(exportList, ", "))
|
|
||||||
}
|
}
|
||||||
return exports, nil
|
return pkgName, exports, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// findImport searches for a package with the given symbols.
|
// findImport searches for a package with the given symbols.
|
||||||
@ -1361,7 +1376,7 @@ func findImport(ctx context.Context, pass *pass, dirScan []*pkg, pkgName string,
|
|||||||
if pass.env.Debug {
|
if pass.env.Debug {
|
||||||
pass.env.Logf("loading exports in dir %s (seeking package %s)", c.pkg.dir, pkgName)
|
pass.env.Logf("loading exports in dir %s (seeking package %s)", c.pkg.dir, pkgName)
|
||||||
}
|
}
|
||||||
exports, err := pass.env.GetResolver().loadExports(ctx, pkgName, c.pkg)
|
exports, err := loadExportsForPackage(ctx, pass.env, pkgName, c.pkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if pass.env.Debug {
|
if pass.env.Debug {
|
||||||
pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
|
pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
|
||||||
@ -1370,10 +1385,15 @@ func findImport(ctx context.Context, pass *pass, dirScan []*pkg, pkgName string,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exportsMap := make(map[string]bool, len(exports))
|
||||||
|
for _, sym := range exports {
|
||||||
|
exportsMap[sym] = true
|
||||||
|
}
|
||||||
|
|
||||||
// If it doesn't have the right
|
// If it doesn't have the right
|
||||||
// symbols, send nil to mean no match.
|
// symbols, send nil to mean no match.
|
||||||
for symbol := range symbols {
|
for symbol := range symbols {
|
||||||
if !exports[symbol] {
|
if !exportsMap[symbol] {
|
||||||
resc <- nil
|
resc <- nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1393,6 +1413,17 @@ func findImport(ctx context.Context, pass *pass, dirScan []*pkg, pkgName string,
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadExportsForPackage(ctx context.Context, env *ProcessEnv, expectPkg string, pkg *pkg) ([]string, error) {
|
||||||
|
pkgName, exports, err := env.GetResolver().loadExports(ctx, pkg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if expectPkg != pkgName {
|
||||||
|
return nil, fmt.Errorf("dir %v is package %v, wanted %v", pkg.dir, pkgName, expectPkg)
|
||||||
|
}
|
||||||
|
return exports, err
|
||||||
|
}
|
||||||
|
|
||||||
// pkgIsCandidate reports whether pkg is a candidate for satisfying the
|
// pkgIsCandidate reports whether pkg is a candidate for satisfying the
|
||||||
// finding which package pkgIdent in the file named by filename is trying
|
// finding which package pkgIdent in the file named by filename is trying
|
||||||
// to refer to.
|
// to refer to.
|
||||||
@ -1524,10 +1555,10 @@ func (fn visitFn) Visit(node ast.Node) ast.Visitor {
|
|||||||
return fn(node)
|
return fn(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyExports(pkg map[string]bool) map[string]bool {
|
func copyExports(pkg []string) map[string]bool {
|
||||||
m := make(map[string]bool, len(pkg))
|
m := make(map[string]bool, len(pkg))
|
||||||
for k, v := range pkg {
|
for _, v := range pkg {
|
||||||
m[k] = v
|
m[v] = true
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
@ -1556,7 +1556,7 @@ var _ = bytes.Buffer
|
|||||||
// Force a scan of the stdlib.
|
// Force a scan of the stdlib.
|
||||||
savedStdlib := stdlib
|
savedStdlib := stdlib
|
||||||
defer func() { stdlib = savedStdlib }()
|
defer func() { stdlib = savedStdlib }()
|
||||||
stdlib = map[string]map[string]bool{}
|
stdlib = map[string][]string{}
|
||||||
|
|
||||||
testConfig{
|
testConfig{
|
||||||
module: packagestest.Module{
|
module: packagestest.Module{
|
||||||
|
@ -44,7 +44,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
outf("// Code generated by mkstdlib.go. DO NOT EDIT.\n\n")
|
outf("// Code generated by mkstdlib.go. DO NOT EDIT.\n\n")
|
||||||
outf("package imports\n")
|
outf("package imports\n")
|
||||||
outf("var stdlib = map[string]map[string]bool{\n")
|
outf("var stdlib = map[string][]string{\n")
|
||||||
f := io.MultiReader(
|
f := io.MultiReader(
|
||||||
mustOpen(api("go1.txt")),
|
mustOpen(api("go1.txt")),
|
||||||
mustOpen(api("go1.1.txt")),
|
mustOpen(api("go1.1.txt")),
|
||||||
@ -89,7 +89,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
sort.Strings(paths)
|
sort.Strings(paths)
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
outf("\t%q: map[string]bool{\n", path)
|
outf("\t%q: []string{\n", path)
|
||||||
pkg := pkgs[path]
|
pkg := pkgs[path]
|
||||||
var syms []string
|
var syms []string
|
||||||
for sym := range pkg {
|
for sym := range pkg {
|
||||||
@ -97,7 +97,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
sort.Strings(syms)
|
sort.Strings(syms)
|
||||||
for _, sym := range syms {
|
for _, sym := range syms {
|
||||||
outf("\t\t%q: true,\n", sym)
|
outf("\t\t%q,\n", sym)
|
||||||
}
|
}
|
||||||
outf("},\n")
|
outf("},\n")
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) {
|
|||||||
// resolution. package main or _test files should count but
|
// resolution. package main or _test files should count but
|
||||||
// don't.
|
// don't.
|
||||||
// TODO(heschi): fix this.
|
// TODO(heschi): fix this.
|
||||||
if _, err := r.cachePackageName(pkgDir); err == nil {
|
if _, err := r.cachePackageName(info); err == nil {
|
||||||
return m, pkgDir
|
return m, pkgDir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,20 +211,18 @@ func (r *ModuleResolver) cacheKeys() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// cachePackageName caches the package name for a dir already in the cache.
|
// cachePackageName caches the package name for a dir already in the cache.
|
||||||
func (r *ModuleResolver) cachePackageName(dir string) (directoryPackageInfo, error) {
|
func (r *ModuleResolver) cachePackageName(info directoryPackageInfo) (directoryPackageInfo, error) {
|
||||||
info, ok := r.cacheLoad(dir)
|
if info.rootType == gopathwalk.RootModuleCache {
|
||||||
if !ok {
|
return r.moduleCacheCache.CachePackageName(info)
|
||||||
panic("cachePackageName on uncached dir " + dir)
|
|
||||||
}
|
}
|
||||||
|
return r.otherCache.CachePackageName(info)
|
||||||
|
}
|
||||||
|
|
||||||
loaded, err := info.reachedStatus(nameLoaded)
|
func (r *ModuleResolver) cacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) {
|
||||||
if loaded {
|
if info.rootType == gopathwalk.RootModuleCache {
|
||||||
return info, err
|
return r.moduleCacheCache.CacheExports(ctx, env, info)
|
||||||
}
|
}
|
||||||
info.packageName, info.err = packageDirToName(info.dir)
|
return r.otherCache.CacheExports(ctx, env, info)
|
||||||
info.status = nameLoaded
|
|
||||||
r.cacheStore(info)
|
|
||||||
return info, info.err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// findModuleByDir returns the module that contains dir, or nil if no such
|
// findModuleByDir returns the module that contains dir, or nil if no such
|
||||||
@ -406,7 +404,7 @@ func (r *ModuleResolver) scan(_ references, loadNames bool, exclude []gopathwalk
|
|||||||
// If we want package names, make sure the cache has them.
|
// If we want package names, make sure the cache has them.
|
||||||
if loadNames {
|
if loadNames {
|
||||||
var err error
|
var err error
|
||||||
if info, err = r.cachePackageName(info.dir); err != nil {
|
if info, err = r.cachePackageName(info); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -465,11 +463,14 @@ func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ModuleResolver) loadExports(ctx context.Context, expectPackage string, pkg *pkg) (map[string]bool, error) {
|
func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg) (string, []string, error) {
|
||||||
if err := r.init(); err != nil {
|
if err := r.init(); err != nil {
|
||||||
return nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
return loadExportsFromFiles(ctx, r.env, expectPackage, pkg.dir)
|
if info, ok := r.cacheLoad(pkg.dir); ok {
|
||||||
|
return r.cacheExports(ctx, r.env, info)
|
||||||
|
}
|
||||||
|
return loadExportsFromFiles(ctx, r.env, pkg.dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) directoryPackageInfo {
|
func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) directoryPackageInfo {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package imports
|
package imports
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/tools/internal/gopathwalk"
|
"golang.org/x/tools/internal/gopathwalk"
|
||||||
@ -30,6 +32,7 @@ const (
|
|||||||
_ directoryPackageStatus = iota
|
_ directoryPackageStatus = iota
|
||||||
directoryScanned
|
directoryScanned
|
||||||
nameLoaded
|
nameLoaded
|
||||||
|
exportsLoaded
|
||||||
)
|
)
|
||||||
|
|
||||||
type directoryPackageInfo struct {
|
type directoryPackageInfo struct {
|
||||||
@ -58,6 +61,10 @@ type directoryPackageInfo struct {
|
|||||||
// Set when status >= nameLoaded.
|
// Set when status >= nameLoaded.
|
||||||
|
|
||||||
packageName string // the package name, as declared in the source.
|
packageName string // the package name, as declared in the source.
|
||||||
|
|
||||||
|
// Set when status >= exportsLoaded.
|
||||||
|
|
||||||
|
exports []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// reachedStatus returns true when info has a status at least target and any error associated with
|
// reachedStatus returns true when info has a status at least target and any error associated with
|
||||||
@ -121,3 +128,38 @@ func (d *dirInfoCache) Keys() (keys []string) {
|
|||||||
}
|
}
|
||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *dirInfoCache) CachePackageName(info directoryPackageInfo) (directoryPackageInfo, error) {
|
||||||
|
if loaded, err := info.reachedStatus(nameLoaded); loaded {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil {
|
||||||
|
return info, fmt.Errorf("cannot read package name, scan error: %v", err)
|
||||||
|
}
|
||||||
|
info.packageName, info.err = packageDirToName(info.dir)
|
||||||
|
info.status = nameLoaded
|
||||||
|
d.Store(info.dir, info)
|
||||||
|
return info, info.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *dirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) {
|
||||||
|
if reached, _ := info.reachedStatus(exportsLoaded); reached {
|
||||||
|
return info.packageName, info.exports, info.err
|
||||||
|
}
|
||||||
|
if reached, err := info.reachedStatus(nameLoaded); reached && err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
info.packageName, info.exports, info.err = loadExportsFromFiles(ctx, env, info.dir)
|
||||||
|
if info.err == context.Canceled {
|
||||||
|
return info.packageName, info.exports, info.err
|
||||||
|
}
|
||||||
|
// The cache structure wants things to proceed linearly. We can skip a
|
||||||
|
// step here, but only if we succeed.
|
||||||
|
if info.status == nameLoaded || info.err == nil {
|
||||||
|
info.status = exportsLoaded
|
||||||
|
} else {
|
||||||
|
info.status = nameLoaded
|
||||||
|
}
|
||||||
|
d.Store(info.dir, info)
|
||||||
|
return info.packageName, info.exports, info.err
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package imports
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -97,7 +98,7 @@ func TestModCacheInfo(t *testing.T) {
|
|||||||
t.Errorf("directory not loaded: %s", d.dir)
|
t.Errorf("directory not loaded: %s", d.dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
if val != d.info {
|
if !reflect.DeepEqual(d.info, val) {
|
||||||
t.Errorf("expected: %v, got: %v", d.info, val)
|
t.Errorf("expected: %v, got: %v", d.info, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user