1
0
mirror of https://github.com/golang/go synced 2024-10-01 04:08:32 -06:00

internal/lsp: use ids instead of package paths as map keys

This adds an IDs map to the metadata cache, which maps package paths to
IDs. This is only ever used by the Import function in the type checker.

Change-Id: I8677d9439895bc6cbca5072e3fa9fddad4e165d5
Reviewed-on: https://go-review.googlesource.com/c/tools/+/181683
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Rebecca Stambler 2019-06-11 18:06:27 -04:00
parent 028e009f40
commit 59534d075a
7 changed files with 79 additions and 65 deletions

View File

@ -22,7 +22,7 @@ type importer struct {
// seen maintains the set of previously imported packages. // seen maintains the set of previously imported packages.
// If we have seen a package that is already in this map, we have a circular import. // If we have seen a package that is already in this map, we have a circular import.
seen map[packagePath]struct{} seen map[packageID]struct{}
// topLevelPkgID is the ID of the package from which type-checking began. // topLevelPkgID is the ID of the package from which type-checking began.
topLevelPkgID packageID topLevelPkgID packageID
@ -32,19 +32,23 @@ type importer struct {
} }
func (imp *importer) Import(pkgPath string) (*types.Package, error) { func (imp *importer) Import(pkgPath string) (*types.Package, error) {
pkg, err := imp.getPkg(packagePath(pkgPath)) id, ok := imp.view.mcache.ids[packagePath(pkgPath)]
if !ok {
return nil, fmt.Errorf("no known ID for %s", pkgPath)
}
pkg, err := imp.getPkg(id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return pkg.types, nil return pkg.types, nil
} }
func (imp *importer) getPkg(pkgPath packagePath) (*pkg, error) { func (imp *importer) getPkg(id packageID) (*pkg, error) {
if _, ok := imp.seen[pkgPath]; ok { if _, ok := imp.seen[id]; ok {
return nil, fmt.Errorf("circular import detected") return nil, fmt.Errorf("circular import detected")
} }
imp.view.pcache.mu.Lock() imp.view.pcache.mu.Lock()
e, ok := imp.view.pcache.packages[pkgPath] e, ok := imp.view.pcache.packages[id]
if ok { if ok {
// cache hit // cache hit
@ -54,12 +58,12 @@ func (imp *importer) getPkg(pkgPath packagePath) (*pkg, error) {
} else { } else {
// cache miss // cache miss
e = &entry{ready: make(chan struct{})} e = &entry{ready: make(chan struct{})}
imp.view.pcache.packages[pkgPath] = e imp.view.pcache.packages[id] = e
imp.view.pcache.mu.Unlock() imp.view.pcache.mu.Unlock()
// This goroutine becomes responsible for populating // This goroutine becomes responsible for populating
// the entry and broadcasting its readiness. // the entry and broadcasting its readiness.
e.pkg, e.err = imp.typeCheck(pkgPath) e.pkg, e.err = imp.typeCheck(id)
close(e.ready) close(e.ready)
} }
@ -68,11 +72,11 @@ func (imp *importer) getPkg(pkgPath packagePath) (*pkg, error) {
if e.err == context.Canceled && imp.ctx.Err() == nil { if e.err == context.Canceled && imp.ctx.Err() == nil {
imp.view.pcache.mu.Lock() imp.view.pcache.mu.Lock()
// Clear out canceled cache entry if it is still there. // Clear out canceled cache entry if it is still there.
if imp.view.pcache.packages[pkgPath] == e { if imp.view.pcache.packages[id] == e {
delete(imp.view.pcache.packages, pkgPath) delete(imp.view.pcache.packages, id)
} }
imp.view.pcache.mu.Unlock() imp.view.pcache.mu.Unlock()
return imp.getPkg(pkgPath) return imp.getPkg(id)
} }
return nil, e.err return nil, e.err
} }
@ -80,10 +84,10 @@ func (imp *importer) getPkg(pkgPath packagePath) (*pkg, error) {
return e.pkg, nil return e.pkg, nil
} }
func (imp *importer) typeCheck(pkgPath packagePath) (*pkg, error) { func (imp *importer) typeCheck(id packageID) (*pkg, error) {
meta, ok := imp.view.mcache.packages[pkgPath] meta, ok := imp.view.mcache.packages[id]
if !ok { if !ok {
return nil, fmt.Errorf("no metadata for %v", pkgPath) return nil, fmt.Errorf("no metadata for %v", id)
} }
pkg := &pkg{ pkg := &pkg{
id: meta.id, id: meta.id,
@ -123,11 +127,11 @@ func (imp *importer) typeCheck(pkgPath packagePath) (*pkg, error) {
pkg.syntax = files pkg.syntax = files
// Handle circular imports by copying previously seen imports. // Handle circular imports by copying previously seen imports.
seen := make(map[packagePath]struct{}) seen := make(map[packageID]struct{})
for k, v := range imp.seen { for k, v := range imp.seen {
seen[k] = v seen[k] = v
} }
seen[pkgPath] = struct{}{} seen[id] = struct{}{}
cfg := &types.Config{ cfg := &types.Config{
Error: func(err error) { Error: func(err error) {
@ -198,7 +202,7 @@ func (imp *importer) cachePackage(ctx context.Context, pkg *pkg, meta *metadata)
if err != nil { if err != nil {
continue continue
} }
pkg.imports[importPath] = importPkg pkg.imports[importPkg.pkgPath] = importPkg
} }
} }

View File

@ -131,9 +131,9 @@ func (f *goFile) GetActiveReverseDeps(ctx context.Context) []source.GoFile {
f.view.mcache.mu.Lock() f.view.mcache.mu.Lock()
defer f.view.mcache.mu.Unlock() defer f.view.mcache.mu.Unlock()
seen := make(map[packagePath]struct{}) // visited packages seen := make(map[packageID]struct{}) // visited packages
results := make(map[*goFile]struct{}) results := make(map[*goFile]struct{})
f.view.reverseDeps(ctx, seen, results, packagePath(pkg.PkgPath())) f.view.reverseDeps(ctx, seen, results, packageID(pkg.ID()))
var files []source.GoFile var files []source.GoFile
for rd := range results { for rd := range results {
@ -149,12 +149,12 @@ func (f *goFile) GetActiveReverseDeps(ctx context.Context) []source.GoFile {
return files return files
} }
func (v *view) reverseDeps(ctx context.Context, seen map[packagePath]struct{}, results map[*goFile]struct{}, pkgPath packagePath) { func (v *view) reverseDeps(ctx context.Context, seen map[packageID]struct{}, results map[*goFile]struct{}, id packageID) {
if _, ok := seen[pkgPath]; ok { if _, ok := seen[id]; ok {
return return
} }
seen[pkgPath] = struct{}{} seen[id] = struct{}{}
m, ok := v.mcache.packages[pkgPath] m, ok := v.mcache.packages[id]
if !ok { if !ok {
return return
} }
@ -164,7 +164,7 @@ func (v *view) reverseDeps(ctx context.Context, seen map[packagePath]struct{}, r
results[f.(*goFile)] = struct{}{} results[f.(*goFile)] = struct{}{}
} }
} }
for parentPkgPath := range m.parents { for parentID := range m.parents {
v.reverseDeps(ctx, seen, results, parentPkgPath) v.reverseDeps(ctx, seen, results, parentID)
} }
} }

View File

@ -18,7 +18,6 @@ func (v *view) loadParseTypecheck(ctx context.Context, f *goFile) ([]packages.Er
if f.astIsTrimmed() { if f.astIsTrimmed() {
f.invalidateAST() f.invalidateAST()
} }
// Save the metadata's current missing imports, if any. // Save the metadata's current missing imports, if any.
var originalMissingImports map[packagePath]struct{} var originalMissingImports map[packagePath]struct{}
if f.meta != nil { if f.meta != nil {
@ -37,21 +36,19 @@ func (v *view) loadParseTypecheck(ctx context.Context, f *goFile) ([]packages.Er
if sameSet(originalMissingImports, f.meta.missingImports) && f.pkg != nil { if sameSet(originalMissingImports, f.meta.missingImports) && f.pkg != nil {
return nil, nil return nil, nil
} }
imp := &importer{ imp := &importer{
view: v, view: v,
seen: make(map[packagePath]struct{}), seen: make(map[packageID]struct{}),
ctx: ctx, ctx: ctx,
fset: f.FileSet(), fset: f.FileSet(),
topLevelPkgID: f.meta.id, topLevelPkgID: f.meta.id,
} }
// Start prefetching direct imports. // Start prefetching direct imports.
for importPath := range f.meta.children { for importID := range f.meta.children {
go imp.Import(string(importPath)) go imp.getPkg(importID)
} }
// Type-check package. // Type-check package.
pkg, err := imp.getPkg(f.meta.pkgPath) pkg, err := imp.getPkg(f.meta.id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -137,24 +134,26 @@ func (v *view) parseImports(ctx context.Context, f *goFile) bool {
} }
func (v *view) link(ctx context.Context, pkgPath packagePath, pkg *packages.Package, parent *metadata) *metadata { func (v *view) link(ctx context.Context, pkgPath packagePath, pkg *packages.Package, parent *metadata) *metadata {
m, ok := v.mcache.packages[pkgPath] id := packageID(pkg.ID)
m, ok := v.mcache.packages[id]
// If a file was added or deleted we need to invalidate the package cache // If a file was added or deleted we need to invalidate the package cache
// so relevant packages get parsed and type checked again. // so relevant packages get parsed and type checked again.
if ok && !filenamesIdentical(m.files, pkg.CompiledGoFiles) { if ok && !filenamesIdentical(m.files, pkg.CompiledGoFiles) {
v.invalidatePackage(pkgPath) v.invalidatePackage(id)
} }
// If we haven't seen this package before.
if !ok { if !ok {
m = &metadata{ m = &metadata{
pkgPath: pkgPath, pkgPath: pkgPath,
id: packageID(pkg.ID), id: id,
typesSizes: pkg.TypesSizes, typesSizes: pkg.TypesSizes,
parents: make(map[packagePath]bool), parents: make(map[packageID]bool),
children: make(map[packagePath]bool), children: make(map[packageID]bool),
missingImports: make(map[packagePath]struct{}), missingImports: make(map[packagePath]struct{}),
} }
v.mcache.packages[pkgPath] = m v.mcache.packages[id] = m
v.mcache.ids[pkgPath] = id
} }
// Reset any field that could have changed across calls to packages.Load. // Reset any field that could have changed across calls to packages.Load.
m.name = pkg.Name m.name = pkg.Name
@ -170,26 +169,29 @@ func (v *view) link(ctx context.Context, pkgPath packagePath, pkg *packages.Pack
} }
// Connect the import graph. // Connect the import graph.
if parent != nil { if parent != nil {
m.parents[parent.pkgPath] = true m.parents[parent.id] = true
parent.children[pkgPath] = true parent.children[id] = true
} }
for importPath, importPkg := range pkg.Imports { for importPath, importPkg := range pkg.Imports {
if len(importPkg.Errors) > 0 { if len(importPkg.Errors) > 0 {
m.missingImports[pkgPath] = struct{}{} m.missingImports[pkgPath] = struct{}{}
} }
importPkgPath := packagePath(importPath) if _, ok := m.children[packageID(importPkg.ID)]; !ok {
if _, ok := m.children[importPkgPath]; !ok { v.link(ctx, packagePath(importPath), importPkg, m)
v.link(ctx, importPkgPath, importPkg, m)
} }
} }
// Clear out any imports that have been removed. // Clear out any imports that have been removed.
for importPath := range m.children { for importID := range m.children {
if _, ok := pkg.Imports[string(importPath)]; !ok { child, ok := v.mcache.packages[importID]
delete(m.children, importPath) if !ok {
if child, ok := v.mcache.packages[importPath]; ok { continue
delete(child.parents, pkgPath)
} }
importPath := string(child.pkgPath)
if _, ok := pkg.Imports[importPath]; ok {
continue
} }
delete(m.children, importID)
delete(child.parents, id)
} }
return m return m
} }

View File

@ -137,6 +137,10 @@ func (pkg *pkg) GetActionGraph(ctx context.Context, a *analysis.Analyzer) (*sour
return e.Action, nil return e.Action, nil
} }
func (pkg *pkg) ID() string {
return string(pkg.id)
}
func (pkg *pkg) PkgPath() string { func (pkg *pkg) PkgPath() string {
return string(pkg.pkgPath) return string(pkg.pkgPath)
} }

View File

@ -81,10 +81,11 @@ func (s *session) NewView(name string, folder span.URI) source.View {
filesByURI: make(map[span.URI]viewFile), filesByURI: make(map[span.URI]viewFile),
filesByBase: make(map[string][]viewFile), filesByBase: make(map[string][]viewFile),
mcache: &metadataCache{ mcache: &metadataCache{
packages: make(map[packagePath]*metadata), packages: make(map[packageID]*metadata),
ids: make(map[packagePath]packageID),
}, },
pcache: &packageCache{ pcache: &packageCache{
packages: make(map[packagePath]*entry), packages: make(map[packageID]*entry),
}, },
ignoredURIs: make(map[span.URI]struct{}), ignoredURIs: make(map[span.URI]struct{}),
} }

View File

@ -70,8 +70,9 @@ type view struct {
} }
type metadataCache struct { type metadataCache struct {
mu sync.Mutex mu sync.Mutex // guards both maps
packages map[packagePath]*metadata packages map[packageID]*metadata
ids map[packagePath]packageID
} }
type metadata struct { type metadata struct {
@ -80,7 +81,7 @@ type metadata struct {
name string name string
files []string files []string
typesSizes types.Sizes typesSizes types.Sizes
parents, children map[packagePath]bool parents, children map[packageID]bool
// missingImports is the set of unresolved imports for this package. // missingImports is the set of unresolved imports for this package.
// It contains any packages with `go list` errors. // It contains any packages with `go list` errors.
@ -89,7 +90,7 @@ type metadata struct {
type packageCache struct { type packageCache struct {
mu sync.Mutex mu sync.Mutex
packages map[packagePath]*entry packages map[packageID]*entry
} }
type entry struct { type entry struct {
@ -247,32 +248,33 @@ func (f *goFile) invalidateAST() {
// Remove the package and all of its reverse dependencies from the cache. // Remove the package and all of its reverse dependencies from the cache.
if f.pkg != nil { if f.pkg != nil {
f.view.remove(f.pkg.pkgPath, map[packagePath]struct{}{}) f.view.remove(f.pkg.id, map[packageID]struct{}{})
} }
} }
// invalidatePackage removes the specified package and dependents from the // invalidatePackage removes the specified package and dependents from the
// package cache. // package cache.
func (v *view) invalidatePackage(pkgPath packagePath) { func (v *view) invalidatePackage(id packageID) {
v.pcache.mu.Lock() v.pcache.mu.Lock()
defer v.pcache.mu.Unlock() defer v.pcache.mu.Unlock()
v.remove(pkgPath, make(map[packagePath]struct{}))
v.remove(id, make(map[packageID]struct{}))
} }
// remove invalidates a package and its reverse dependencies in the view's // remove invalidates a package and its reverse dependencies in the view's
// package cache. It is assumed that the caller has locked both the mutexes // package cache. It is assumed that the caller has locked both the mutexes
// of both the mcache and the pcache. // of both the mcache and the pcache.
func (v *view) remove(pkgPath packagePath, seen map[packagePath]struct{}) { func (v *view) remove(id packageID, seen map[packageID]struct{}) {
if _, ok := seen[pkgPath]; ok { if _, ok := seen[id]; ok {
return return
} }
m, ok := v.mcache.packages[pkgPath] m, ok := v.mcache.packages[id]
if !ok { if !ok {
return return
} }
seen[pkgPath] = struct{}{} seen[id] = struct{}{}
for parentPkgPath := range m.parents { for parentID := range m.parents {
v.remove(parentPkgPath, seen) v.remove(parentID, seen)
} }
// All of the files in the package may also be holding a pointer to the // All of the files in the package may also be holding a pointer to the
// invalidated package. // invalidated package.
@ -283,7 +285,7 @@ func (v *view) remove(pkgPath packagePath, seen map[packagePath]struct{}) {
} }
} }
} }
delete(v.pcache.packages, pkgPath) delete(v.pcache.packages, id)
} }
// FindFile returns the file if the given URI is already a part of the view. // FindFile returns the file if the given URI is already a part of the view.

View File

@ -220,6 +220,7 @@ type SumFile interface {
// Package represents a Go package that has been type-checked. It maintains // Package represents a Go package that has been type-checked. It maintains
// only the relevant fields of a *go/packages.Package. // only the relevant fields of a *go/packages.Package.
type Package interface { type Package interface {
ID() string
PkgPath() string PkgPath() string
GetFilenames() []string GetFilenames() []string
GetSyntax() []*ast.File GetSyntax() []*ast.File