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

internal/lsp/cache: let gopls track go.mod files

We used to read the go.mod file information out of the imports.Resolver.
Now that gopls tracks go.mod itself, we can use that instead. This is a
slight regression, in that go.mods in replace targets will no longer be
watched, but I don't think that's too important.

This allows us to stop reading the ModuleResolver's internals, which
were not sufficiently locked.

Updates golang/go#36605.

Change-Id: I42939e0248cba1f6b3850a003de67fcc11ab10b1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/215319
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:
Heschi Kreinick 2020-01-17 15:33:57 -05:00
parent 633b092c1e
commit 354bea8ca8
2 changed files with 30 additions and 64 deletions

View File

@ -29,10 +29,10 @@ type ModuleResolver struct {
scanSema chan struct{} // scanSema prevents concurrent scans and guards scannedRoots. scanSema chan struct{} // scanSema prevents concurrent scans and guards scannedRoots.
scannedRoots map[gopathwalk.Root]bool scannedRoots map[gopathwalk.Root]bool
Initialized bool initialized bool
Main *ModuleJSON main *ModuleJSON
ModsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path... modsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path...
ModsByDir []*ModuleJSON // ...or Dir. modsByDir []*ModuleJSON // ...or Dir.
// moduleCacheCache stores information about the module cache. // moduleCacheCache stores information about the module cache.
moduleCacheCache *dirInfoCache moduleCacheCache *dirInfoCache
@ -59,7 +59,7 @@ func newModuleResolver(e *ProcessEnv) *ModuleResolver {
} }
func (r *ModuleResolver) init() error { func (r *ModuleResolver) init() error {
if r.Initialized { if r.initialized {
return nil return nil
} }
mainMod, vendorEnabled, err := vendorEnabled(r.env) mainMod, vendorEnabled, err := vendorEnabled(r.env)
@ -70,13 +70,13 @@ func (r *ModuleResolver) init() error {
if mainMod != nil && vendorEnabled { if mainMod != nil && vendorEnabled {
// Vendor mode is on, so all the non-Main modules are irrelevant, // Vendor mode is on, so all the non-Main modules are irrelevant,
// and we need to search /vendor for everything. // and we need to search /vendor for everything.
r.Main = mainMod r.main = mainMod
r.dummyVendorMod = &ModuleJSON{ r.dummyVendorMod = &ModuleJSON{
Path: "", Path: "",
Dir: filepath.Join(mainMod.Dir, "vendor"), Dir: filepath.Join(mainMod.Dir, "vendor"),
} }
r.ModsByModPath = []*ModuleJSON{mainMod, r.dummyVendorMod} r.modsByModPath = []*ModuleJSON{mainMod, r.dummyVendorMod}
r.ModsByDir = []*ModuleJSON{mainMod, r.dummyVendorMod} r.modsByDir = []*ModuleJSON{mainMod, r.dummyVendorMod}
} else { } else {
// Vendor mode is off, so run go list -m ... to find everything. // Vendor mode is off, so run go list -m ... to find everything.
r.initAllMods() r.initAllMods()
@ -84,15 +84,15 @@ func (r *ModuleResolver) init() error {
r.moduleCacheDir = filepath.Join(filepath.SplitList(r.env.GOPATH)[0], "/pkg/mod") r.moduleCacheDir = filepath.Join(filepath.SplitList(r.env.GOPATH)[0], "/pkg/mod")
sort.Slice(r.ModsByModPath, func(i, j int) bool { sort.Slice(r.modsByModPath, func(i, j int) bool {
count := func(x int) int { count := func(x int) int {
return strings.Count(r.ModsByModPath[x].Path, "/") return strings.Count(r.modsByModPath[x].Path, "/")
} }
return count(j) < count(i) // descending order return count(j) < count(i) // descending order
}) })
sort.Slice(r.ModsByDir, func(i, j int) bool { sort.Slice(r.modsByDir, func(i, j int) bool {
count := func(x int) int { count := func(x int) int {
return strings.Count(r.ModsByDir[x].Dir, "/") return strings.Count(r.modsByDir[x].Dir, "/")
} }
return count(j) < count(i) // descending order return count(j) < count(i) // descending order
}) })
@ -100,8 +100,8 @@ func (r *ModuleResolver) init() error {
r.roots = []gopathwalk.Root{ r.roots = []gopathwalk.Root{
{filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT}, {filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT},
} }
if r.Main != nil { if r.main != nil {
r.roots = append(r.roots, gopathwalk.Root{r.Main.Dir, gopathwalk.RootCurrentModule}) r.roots = append(r.roots, gopathwalk.Root{r.main.Dir, gopathwalk.RootCurrentModule})
} }
if vendorEnabled { if vendorEnabled {
r.roots = append(r.roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther}) r.roots = append(r.roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther})
@ -115,12 +115,12 @@ func (r *ModuleResolver) init() error {
} }
} }
// Walk dependent modules before scanning the full mod cache, direct deps first. // Walk dependent modules before scanning the full mod cache, direct deps first.
for _, mod := range r.ModsByModPath { for _, mod := range r.modsByModPath {
if !mod.Indirect && !mod.Main { if !mod.Indirect && !mod.Main {
addDep(mod) addDep(mod)
} }
} }
for _, mod := range r.ModsByModPath { for _, mod := range r.modsByModPath {
if mod.Indirect && !mod.Main { if mod.Indirect && !mod.Main {
addDep(mod) addDep(mod)
} }
@ -141,7 +141,7 @@ func (r *ModuleResolver) init() error {
listeners: map[*int]cacheListener{}, listeners: map[*int]cacheListener{},
} }
} }
r.Initialized = true r.initialized = true
return nil return nil
} }
@ -162,10 +162,10 @@ func (r *ModuleResolver) initAllMods() error {
// Can't do anything with a module that's not downloaded. // Can't do anything with a module that's not downloaded.
continue continue
} }
r.ModsByModPath = append(r.ModsByModPath, mod) r.modsByModPath = append(r.modsByModPath, mod)
r.ModsByDir = append(r.ModsByDir, mod) r.modsByDir = append(r.modsByDir, mod)
if mod.Main { if mod.Main {
r.Main = mod r.main = mod
} }
} }
return nil return nil
@ -198,7 +198,7 @@ func (r *ModuleResolver) ClearForNewMod() {
func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) { func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) {
// This can't find packages in the stdlib, but that's harmless for all // This can't find packages in the stdlib, but that's harmless for all
// the existing code paths. // the existing code paths.
for _, m := range r.ModsByModPath { for _, m := range r.modsByModPath {
if !strings.HasPrefix(importPath, m.Path) { if !strings.HasPrefix(importPath, m.Path) {
continue continue
} }
@ -292,7 +292,7 @@ func (r *ModuleResolver) findModuleByDir(dir string) *ModuleJSON {
// - in /vendor/ in -mod=vendor mode. // - in /vendor/ in -mod=vendor mode.
// - nested module? Dunno. // - nested module? Dunno.
// Rumor has it that replace targets cannot contain other replace targets. // Rumor has it that replace targets cannot contain other replace targets.
for _, m := range r.ModsByDir { for _, m := range r.modsByDir {
if !strings.HasPrefix(dir, m.Dir) { if !strings.HasPrefix(dir, m.Dir) {
continue continue
} }
@ -570,7 +570,7 @@ func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) dir
} }
switch root.Type { switch root.Type {
case gopathwalk.RootCurrentModule: case gopathwalk.RootCurrentModule:
importPath = path.Join(r.Main.Path, filepath.ToSlash(subdir)) importPath = path.Join(r.main.Path, filepath.ToSlash(subdir))
case gopathwalk.RootModuleCache: case gopathwalk.RootModuleCache:
matches := modCacheRegexp.FindStringSubmatch(subdir) matches := modCacheRegexp.FindStringSubmatch(subdir)
if len(matches) == 0 { if len(matches) == 0 {

View File

@ -69,11 +69,7 @@ type view struct {
processEnv *imports.ProcessEnv processEnv *imports.ProcessEnv
cacheRefreshDuration time.Duration cacheRefreshDuration time.Duration
cacheRefreshTimer *time.Timer cacheRefreshTimer *time.Timer
cachedModFileVersion source.FileIdentity
// modFileVersions stores the last seen versions of the module files that are used
// by processEnvs resolver.
// TODO(suzmue): These versions may not actually be on disk.
modFileVersions map[string]string
// keep track of files by uri and by basename, a single file may be mapped // keep track of files by uri and by basename, a single file may be mapped
// to multiple uris, and the same basename may map to multiple files // to multiple uris, and the same basename may map to multiple files
@ -287,9 +283,12 @@ func (v *view) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options)
} }
} }
// Before running the user provided function, clear caches in the resolver. // In module mode, check if the mod file has changed.
if v.modFilesChanged() { if mod, _, err := v.Snapshot().ModFiles(ctx); err == nil && mod != nil {
v.processEnv.GetResolver().(*imports.ModuleResolver).ClearForNewMod() if mod.Identity() != v.cachedModFileVersion {
v.processEnv.GetResolver().(*imports.ModuleResolver).ClearForNewMod()
}
v.cachedModFileVersion = mod.Identity()
} }
// Run the user function. // Run the user function.
@ -308,10 +307,6 @@ func (v *view) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options)
return err return err
} }
// If applicable, store the file versions of the 'go.mod' files that are
// looked at by the resolver.
v.storeModFileVersions()
if v.cacheRefreshTimer == nil { if v.cacheRefreshTimer == nil {
// Don't refresh more than twice per minute. // Don't refresh more than twice per minute.
delay := 30 * time.Second delay := 30 * time.Second
@ -377,35 +372,6 @@ func (v *view) buildProcessEnv(ctx context.Context) (*imports.ProcessEnv, error)
return env, nil return env, nil
} }
func (v *view) modFilesChanged() bool {
// Check the versions of the 'go.mod' files of the main module
// and modules included by a replace directive. Return true if
// any of these file versions do not match.
for filename, version := range v.modFileVersions {
if version != v.fileVersion(filename) {
return true
}
}
return false
}
func (v *view) storeModFileVersions() {
// Store the mod files versions, if we are using a ModuleResolver.
r, moduleMode := v.processEnv.GetResolver().(*imports.ModuleResolver)
if !moduleMode || !r.Initialized {
return
}
v.modFileVersions = make(map[string]string)
// Get the file versions of the 'go.mod' files of the main module
// and modules included by a replace directive in the resolver.
for _, mod := range r.ModsByModPath {
if (mod.Main || mod.Replace != nil) && mod.GoMod != "" {
v.modFileVersions[mod.GoMod] = v.fileVersion(mod.GoMod)
}
}
}
func (v *view) fileVersion(filename string) string { func (v *view) fileVersion(filename string) string {
uri := span.FileURI(filename) uri := span.FileURI(filename)
fh := v.session.GetFile(uri) fh := v.session.GetFile(uri)