1
0
mirror of https://github.com/golang/go synced 2024-11-18 13:04:46 -07:00

internal/imports: cache module information

To avoid proposing unusable completions, such as those in modules that
need a replace statement to be usable, we need to know what module a
directory is in. That involves walking up the directory tree to find a
go.mod file, which is expensive to do over and over. Really, we just
need to check if the directory we're in has a go.mod file, then use the
parent dir's results.

Add module information to the cache and use it when figuring out what
module a dir is in.

Change-Id: Ia74ba9b37d73fca5e6786a94c73c8fd71b591645
Reviewed-on: https://go-review.googlesource.com/c/tools/+/202541
Run-TryBot: Heschi Kreinick <heschi@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Heschi Kreinick 2019-10-21 17:48:25 -04:00
parent 3057e18543
commit 7178990c25
3 changed files with 32 additions and 15 deletions

View File

@ -249,28 +249,41 @@ func (r *ModuleResolver) dirIsNestedModule(dir string, mod *ModuleJSON) bool {
// so it cannot be a nested module.
return false
}
mf := r.findModFile(dir)
if mf == "" {
modDir, _ := r.modInfo(dir)
if modDir == "" {
return false
}
return filepath.Dir(mf) != mod.Dir
return modDir != mod.Dir
}
func (r *ModuleResolver) findModFile(dir string) string {
func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) {
readModName := func(modFile string) string {
modBytes, err := ioutil.ReadFile(modFile)
if err != nil {
return ""
}
return modulePath(modBytes)
}
if r.dirInModuleCache(dir) {
matches := modCacheRegexp.FindStringSubmatch(dir)
index := strings.Index(dir, matches[1]+"@"+matches[2])
return filepath.Join(dir[:index], matches[1]+"@"+matches[2], "go.mod")
modDir := filepath.Join(dir[:index], matches[1]+"@"+matches[2])
return modDir, readModName(filepath.Join(modDir, "go.mod"))
}
for {
if info, ok := r.cacheLoad(dir); ok {
return info.moduleDir, info.moduleName
}
f := filepath.Join(dir, "go.mod")
info, err := os.Stat(f)
if err == nil && !info.IsDir() {
return f
return dir, readModName(f)
}
d := filepath.Dir(dir)
if len(d) >= len(dir) {
return "" // reached top of file system, no go.mod
return "", "" // reached top of file system, no go.mod
}
dir = d
}
@ -478,22 +491,22 @@ func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) dir
importPath = subdir
}
modDir, modName := r.modInfo(dir)
result := directoryPackageInfo{
status: directoryScanned,
dir: dir,
rootType: root.Type,
nonCanonicalImportPath: importPath,
needsReplace: false,
moduleDir: modDir,
moduleName: modName,
}
if root.Type == gopathwalk.RootGOROOT {
// stdlib packages are always in scope, despite the confusing go.mod
return result
}
// Check that this package is not obviously impossible to import.
modFile := r.findModFile(dir)
modBytes, err := ioutil.ReadFile(modFile)
if err == nil && !strings.HasPrefix(importPath, modulePath(modBytes)) {
if !strings.HasPrefix(importPath, modName) {
// The module's declared path does not match
// its expected path. It probably needs a
// replace directive we don't have.

View File

@ -51,6 +51,10 @@ type directoryPackageInfo struct {
// replace directive.
needsReplace bool
// Module-related information.
moduleDir string // The directory that is the module root of this dir.
moduleName string // The module name that contains this dir.
// Set when status >= nameLoaded.
packageName string // the package name, as declared in the source.

View File

@ -765,12 +765,12 @@ package x
import _ "rsc.io/quote"
`, "")
defer mt.cleanup()
want := filepath.Join(mt.resolver.env.GOPATH, "pkg/mod", "rsc.io/quote@v1.5.2", "go.mod")
want := filepath.Join(mt.resolver.env.GOPATH, "pkg/mod", "rsc.io/quote@v1.5.2")
found := mt.assertScanFinds("rsc.io/quote", "quote")
modFile := mt.resolver.findModFile(found.dir)
if modFile != want {
t.Errorf("expected: %s, got: %s", want, modFile)
modDir, _ := mt.resolver.modInfo(found.dir)
if modDir != want {
t.Errorf("expected: %s, got: %s", want, modDir)
}
}