internal/lsp: create types for package paths and IDs

This change replaces the strings that were previously used for both the
ID and package path fields. This is a precursor to the change that will
replace the uses of package path with package ID.

Change-Id: I353e98aedede9b85c7a183fdd49048ff43b1e26d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/181757
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 17:09:26 -04:00
parent d303ba255a
commit 6050b95026
6 changed files with 60 additions and 58 deletions

View File

@ -22,24 +22,24 @@ 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[string]struct{} seen map[packagePath]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 string topLevelPkgID packageID
ctx context.Context ctx context.Context
fset *token.FileSet fset *token.FileSet
} }
func (imp *importer) Import(pkgPath string) (*types.Package, error) { func (imp *importer) Import(pkgPath string) (*types.Package, error) {
pkg, err := imp.getPkg(pkgPath) pkg, err := imp.getPkg(packagePath(pkgPath))
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 string) (*pkg, error) { func (imp *importer) getPkg(pkgPath packagePath) (*pkg, error) {
if _, ok := imp.seen[pkgPath]; ok { if _, ok := imp.seen[pkgPath]; ok {
return nil, fmt.Errorf("circular import detected") return nil, fmt.Errorf("circular import detected")
} }
@ -80,7 +80,7 @@ func (imp *importer) getPkg(pkgPath string) (*pkg, error) {
return e.pkg, nil return e.pkg, nil
} }
func (imp *importer) typeCheck(pkgPath string) (*pkg, error) { func (imp *importer) typeCheck(pkgPath packagePath) (*pkg, error) {
meta, ok := imp.view.mcache.packages[pkgPath] meta, ok := imp.view.mcache.packages[pkgPath]
if !ok { if !ok {
return nil, fmt.Errorf("no metadata for %v", pkgPath) return nil, fmt.Errorf("no metadata for %v", pkgPath)
@ -89,7 +89,7 @@ func (imp *importer) typeCheck(pkgPath string) (*pkg, error) {
id: meta.id, id: meta.id,
pkgPath: meta.pkgPath, pkgPath: meta.pkgPath,
files: meta.files, files: meta.files,
imports: make(map[string]*pkg), imports: make(map[packagePath]*pkg),
typesSizes: meta.typesSizes, typesSizes: meta.typesSizes,
typesInfo: &types.Info{ typesInfo: &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue), Types: make(map[ast.Expr]types.TypeAndValue),
@ -117,13 +117,13 @@ func (imp *importer) typeCheck(pkgPath string) (*pkg, error) {
} else if len(files) == 0 { // not the unsafe package, no parsed files } else if len(files) == 0 { // not the unsafe package, no parsed files
return nil, fmt.Errorf("no parsed files for package %s", pkg.pkgPath) return nil, fmt.Errorf("no parsed files for package %s", pkg.pkgPath)
} else { } else {
pkg.types = types.NewPackage(meta.pkgPath, meta.name) pkg.types = types.NewPackage(string(meta.pkgPath), meta.name)
} }
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[string]struct{}) seen := make(map[packagePath]struct{})
for k, v := range imp.seen { for k, v := range imp.seen {
seen[k] = v seen[k] = v
} }

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[string]struct{}) // visited packages seen := make(map[packagePath]struct{}) // visited packages
results := make(map[*goFile]struct{}) results := make(map[*goFile]struct{})
f.view.reverseDeps(ctx, seen, results, pkg.PkgPath()) f.view.reverseDeps(ctx, seen, results, packagePath(pkg.PkgPath()))
var files []source.GoFile var files []source.GoFile
for rd := range results { for rd := range results {
@ -149,7 +149,7 @@ func (f *goFile) GetActiveReverseDeps(ctx context.Context) []source.GoFile {
return files return files
} }
func (v *view) reverseDeps(ctx context.Context, seen map[string]struct{}, results map[*goFile]struct{}, pkgPath string) { func (v *view) reverseDeps(ctx context.Context, seen map[packagePath]struct{}, results map[*goFile]struct{}, pkgPath packagePath) {
if _, ok := seen[pkgPath]; ok { if _, ok := seen[pkgPath]; ok {
return return
} }

View File

@ -20,7 +20,7 @@ func (v *view) loadParseTypecheck(ctx context.Context, f *goFile) ([]packages.Er
} }
// Save the metadata's current missing imports, if any. // Save the metadata's current missing imports, if any.
var originalMissingImports map[string]struct{} var originalMissingImports map[packagePath]struct{}
if f.meta != nil { if f.meta != nil {
originalMissingImports = f.meta.missingImports originalMissingImports = f.meta.missingImports
} }
@ -40,7 +40,7 @@ func (v *view) loadParseTypecheck(ctx context.Context, f *goFile) ([]packages.Er
imp := &importer{ imp := &importer{
view: v, view: v,
seen: make(map[string]struct{}), seen: make(map[packagePath]struct{}),
ctx: ctx, ctx: ctx,
fset: f.FileSet(), fset: f.FileSet(),
topLevelPkgID: f.meta.id, topLevelPkgID: f.meta.id,
@ -48,7 +48,7 @@ func (v *view) loadParseTypecheck(ctx context.Context, f *goFile) ([]packages.Er
// Start prefetching direct imports. // Start prefetching direct imports.
for importPath := range f.meta.children { for importPath := range f.meta.children {
go imp.Import(importPath) go imp.Import(string(importPath))
} }
// Type-check package. // Type-check package.
pkg, err := imp.getPkg(f.meta.pkgPath) pkg, err := imp.getPkg(f.meta.pkgPath)
@ -65,7 +65,7 @@ func (v *view) loadParseTypecheck(ctx context.Context, f *goFile) ([]packages.Er
return nil, nil return nil, nil
} }
func sameSet(x, y map[string]struct{}) bool { func sameSet(x, y map[packagePath]struct{}) bool {
if len(x) != len(y) { if len(x) != len(y) {
return false return false
} }
@ -103,7 +103,7 @@ func (v *view) checkMetadata(ctx context.Context, f *goFile) ([]packages.Error,
return pkg.Errors, fmt.Errorf("package %s has errors, skipping type-checking", pkg.PkgPath) return pkg.Errors, fmt.Errorf("package %s has errors, skipping type-checking", pkg.PkgPath)
} }
// Build the import graph for this package. // Build the import graph for this package.
v.link(ctx, pkg.PkgPath, pkg, nil) v.link(ctx, packagePath(pkg.PkgPath), pkg, nil)
} }
return nil, nil return nil, nil
} }
@ -136,16 +136,16 @@ func (v *view) parseImports(ctx context.Context, f *goFile) bool {
return false return false
} }
func (v *view) link(ctx context.Context, pkgPath string, 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] m, ok := v.mcache.packages[pkgPath]
if !ok { if !ok {
m = &metadata{ m = &metadata{
pkgPath: pkgPath, pkgPath: pkgPath,
id: pkg.ID, id: packageID(pkg.ID),
typesSizes: pkg.TypesSizes, typesSizes: pkg.TypesSizes,
parents: make(map[string]bool), parents: make(map[packagePath]bool),
children: make(map[string]bool), children: make(map[packagePath]bool),
missingImports: make(map[string]struct{}), missingImports: make(map[packagePath]struct{}),
} }
v.mcache.packages[pkgPath] = m v.mcache.packages[pkgPath] = m
} }
@ -168,15 +168,16 @@ func (v *view) link(ctx context.Context, pkgPath string, pkg *packages.Package,
} }
for importPath, importPkg := range pkg.Imports { for importPath, importPkg := range pkg.Imports {
if len(importPkg.Errors) > 0 { if len(importPkg.Errors) > 0 {
m.missingImports[pkg.PkgPath] = struct{}{} m.missingImports[pkgPath] = struct{}{}
} }
if _, ok := m.children[importPath]; !ok { importPkgPath := packagePath(importPath)
v.link(ctx, importPath, importPkg, m) if _, ok := m.children[importPkgPath]; !ok {
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 importPath := range m.children {
if _, ok := pkg.Imports[importPath]; !ok { if _, ok := pkg.Imports[string(importPath)]; !ok {
delete(m.children, importPath) delete(m.children, importPath)
if child, ok := v.mcache.packages[importPath]; ok { if child, ok := v.mcache.packages[importPath]; ok {
delete(child.parents, pkgPath) delete(child.parents, pkgPath)

View File

@ -18,14 +18,17 @@ import (
// pkg contains the type information needed by the source package. // pkg contains the type information needed by the source package.
type pkg struct { type pkg struct {
id, pkgPath string // ID and package path have their own types to avoid being used interchangeably.
files []string id packageID
syntax []*astFile pkgPath packagePath
errors []packages.Error
imports map[string]*pkg files []string
types *types.Package syntax []*astFile
typesInfo *types.Info errors []packages.Error
typesSizes types.Sizes imports map[packagePath]*pkg
types *types.Package
typesInfo *types.Info
typesSizes types.Sizes
// The analysis cache holds analysis information for all the packages in a view. // The analysis cache holds analysis information for all the packages in a view.
// Each graph node (action) is one unit of analysis. // Each graph node (action) is one unit of analysis.
@ -35,6 +38,12 @@ type pkg struct {
analyses map[*analysis.Analyzer]*analysisEntry analyses map[*analysis.Analyzer]*analysisEntry
} }
// packageID is a type that abstracts a package ID.
type packageID string
// packagePath is a type that abstracts a package path.
type packagePath string
type analysisEntry struct { type analysisEntry struct {
done chan struct{} done chan struct{}
succeeded bool succeeded bool
@ -108,11 +117,11 @@ func (pkg *pkg) GetActionGraph(ctx context.Context, a *analysis.Analyzer) (*sour
if len(a.FactTypes) > 0 { if len(a.FactTypes) > 0 {
importPaths := make([]string, 0, len(pkg.imports)) importPaths := make([]string, 0, len(pkg.imports))
for importPath := range pkg.imports { for importPath := range pkg.imports {
importPaths = append(importPaths, importPath) importPaths = append(importPaths, string(importPath))
} }
sort.Strings(importPaths) // for determinism sort.Strings(importPaths) // for determinism
for _, importPath := range importPaths { for _, importPath := range importPaths {
dep, ok := pkg.imports[importPath] dep, ok := pkg.imports[packagePath(importPath)]
if !ok { if !ok {
continue continue
} }
@ -129,7 +138,7 @@ func (pkg *pkg) GetActionGraph(ctx context.Context, a *analysis.Analyzer) (*sour
} }
func (pkg *pkg) PkgPath() string { func (pkg *pkg) PkgPath() string {
return pkg.pkgPath return string(pkg.pkgPath)
} }
func (pkg *pkg) GetFilenames() []string { func (pkg *pkg) GetFilenames() []string {
@ -165,7 +174,7 @@ func (pkg *pkg) IsIllTyped() bool {
} }
func (pkg *pkg) GetImport(pkgPath string) source.Package { func (pkg *pkg) GetImport(pkgPath string) source.Package {
if imp := pkg.imports[pkgPath]; imp != nil { if imp := pkg.imports[packagePath(pkgPath)]; imp != nil {
return imp return imp
} }
// Don't return a nil pointer because that still satisfies the interface. // Don't return a nil pointer because that still satisfies the interface.

View File

@ -81,10 +81,10 @@ 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[string]*metadata), packages: make(map[packagePath]*metadata),
}, },
pcache: &packageCache{ pcache: &packageCache{
packages: make(map[string]*entry), packages: make(map[packagePath]*entry),
}, },
ignoredURIs: make(map[span.URI]struct{}), ignoredURIs: make(map[span.URI]struct{}),
} }

View File

@ -71,23 +71,25 @@ type view struct {
type metadataCache struct { type metadataCache struct {
mu sync.Mutex mu sync.Mutex
packages map[string]*metadata packages map[packagePath]*metadata
} }
type metadata struct { type metadata struct {
id, pkgPath, name string id packageID
pkgPath packagePath
name string
files []string files []string
typesSizes types.Sizes typesSizes types.Sizes
parents, children map[string]bool parents, children map[packagePath]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.
missingImports map[string]struct{} missingImports map[packagePath]struct{}
} }
type packageCache struct { type packageCache struct {
mu sync.Mutex mu sync.Mutex
packages map[string]*entry packages map[packagePath]*entry
} }
type entry struct { type entry struct {
@ -227,20 +229,10 @@ func (v *view) SetContent(ctx context.Context, uri span.URI, content []byte) err
// invalidateContent invalidates the content of a Go file, // invalidateContent invalidates the content of a Go file,
// including any position and type information that depends on it. // including any position and type information that depends on it.
func (f *goFile) invalidateContent() { func (f *goFile) invalidateContent() {
f.view.pcache.mu.Lock()
f.handleMu.Lock() f.handleMu.Lock()
defer func() { defer f.handleMu.Unlock()
f.handleMu.Unlock()
f.view.pcache.mu.Unlock()
}()
f.ast = nil f.invalidateAST()
f.token = nil
// Remove the package and all of its reverse dependencies from the cache.
if f.pkg != nil {
f.view.remove(f.pkg.pkgPath, map[string]struct{}{})
}
f.handle = nil f.handle = nil
} }
@ -255,14 +247,14 @@ 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[string]struct{}{}) f.view.remove(f.pkg.pkgPath, map[packagePath]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 string, seen map[string]struct{}) { func (v *view) remove(pkgPath packagePath, seen map[packagePath]struct{}) {
if _, ok := seen[pkgPath]; ok { if _, ok := seen[pkgPath]; ok {
return return
} }