mirror of
https://github.com/golang/go
synced 2024-11-19 07:04:43 -07:00
63859f3815
Now the "type" of a *ast.PkgName is the package it points to. Of course, a package is not a real types.Type, but we can still jump you there. We have to pick one of the package's files, so we choose the longest one, hoping it is the most interesting. Similarly, the "definition" of an *ast.ImportSpec is the package being imported. I also added a nil check for the package in SignatureHelp. This panics for me occasionally. Change-Id: Ide4640530a28bcec9da6de36723eb7f0e4cc941c GitHub-Last-Rev: 8190baa0b908065db5b53f236de03d2f3bff39b5 GitHub-Pull-Request: golang/tools#92 Reviewed-on: https://go-review.googlesource.com/c/tools/+/174081 Run-TryBot: Rebecca Stambler <rstambler@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
168 lines
3.9 KiB
Go
168 lines
3.9 KiB
Go
package cache
|
|
|
|
import (
|
|
"context"
|
|
"go/ast"
|
|
"go/types"
|
|
"sort"
|
|
"sync"
|
|
|
|
"golang.org/x/tools/go/analysis"
|
|
"golang.org/x/tools/go/packages"
|
|
"golang.org/x/tools/internal/lsp/source"
|
|
)
|
|
|
|
// Package contains the type information needed by the source package.
|
|
type Package struct {
|
|
id, pkgPath string
|
|
files []string
|
|
syntax []*ast.File
|
|
errors []packages.Error
|
|
imports map[string]*Package
|
|
types *types.Package
|
|
typesInfo *types.Info
|
|
typesSizes types.Sizes
|
|
|
|
// The analysis cache holds analysis information for all the packages in a view.
|
|
// Each graph node (action) is one unit of analysis.
|
|
// Edges express package-to-package (vertical) dependencies,
|
|
// and analysis-to-analysis (horizontal) dependencies.
|
|
mu sync.Mutex
|
|
analyses map[*analysis.Analyzer]*analysisEntry
|
|
}
|
|
|
|
type analysisEntry struct {
|
|
done chan struct{}
|
|
succeeded bool
|
|
*source.Action
|
|
}
|
|
|
|
func (pkg *Package) GetActionGraph(ctx context.Context, a *analysis.Analyzer) (*source.Action, error) {
|
|
if ctx.Err() != nil {
|
|
return nil, ctx.Err()
|
|
}
|
|
|
|
pkg.mu.Lock()
|
|
e, ok := pkg.analyses[a]
|
|
if ok {
|
|
// cache hit
|
|
pkg.mu.Unlock()
|
|
|
|
// wait for entry to become ready or the context to be cancelled
|
|
select {
|
|
case <-e.done:
|
|
// If the goroutine we are waiting on was cancelled, we should retry.
|
|
// If errors other than cancelation/timeout become possible, it may
|
|
// no longer be appropriate to always retry here.
|
|
if !e.succeeded {
|
|
return pkg.GetActionGraph(ctx, a)
|
|
}
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
}
|
|
} else {
|
|
// cache miss
|
|
e = &analysisEntry{
|
|
done: make(chan struct{}),
|
|
Action: &source.Action{
|
|
Analyzer: a,
|
|
Pkg: pkg,
|
|
},
|
|
}
|
|
pkg.analyses[a] = e
|
|
pkg.mu.Unlock()
|
|
|
|
defer func() {
|
|
// If we got an error, clear out our defunct cache entry. We don't cache
|
|
// errors since they could depend on our dependencies, which can change.
|
|
// Currently the only possible error is context.Canceled, though, which
|
|
// should also not be cached.
|
|
if !e.succeeded {
|
|
pkg.mu.Lock()
|
|
delete(pkg.analyses, a)
|
|
pkg.mu.Unlock()
|
|
}
|
|
|
|
// Always close done so waiters don't get stuck.
|
|
close(e.done)
|
|
}()
|
|
|
|
// This goroutine becomes responsible for populating
|
|
// the entry and broadcasting its readiness.
|
|
|
|
// Add a dependency on each required analyzers.
|
|
for _, req := range a.Requires {
|
|
act, err := pkg.GetActionGraph(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e.Deps = append(e.Deps, act)
|
|
}
|
|
|
|
// An analysis that consumes/produces facts
|
|
// must run on the package's dependencies too.
|
|
if len(a.FactTypes) > 0 {
|
|
importPaths := make([]string, 0, len(pkg.imports))
|
|
for importPath := range pkg.imports {
|
|
importPaths = append(importPaths, importPath)
|
|
}
|
|
sort.Strings(importPaths) // for determinism
|
|
for _, importPath := range importPaths {
|
|
dep, ok := pkg.imports[importPath]
|
|
if !ok {
|
|
continue
|
|
}
|
|
act, err := dep.GetActionGraph(ctx, a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e.Deps = append(e.Deps, act)
|
|
}
|
|
}
|
|
e.succeeded = true
|
|
}
|
|
return e.Action, nil
|
|
}
|
|
|
|
func (pkg *Package) PkgPath() string {
|
|
return pkg.pkgPath
|
|
}
|
|
|
|
func (pkg *Package) GetFilenames() []string {
|
|
return pkg.files
|
|
}
|
|
|
|
func (pkg *Package) GetSyntax() []*ast.File {
|
|
return pkg.syntax
|
|
}
|
|
|
|
func (pkg *Package) GetErrors() []packages.Error {
|
|
return pkg.errors
|
|
}
|
|
|
|
func (pkg *Package) GetTypes() *types.Package {
|
|
return pkg.types
|
|
}
|
|
|
|
func (pkg *Package) GetTypesInfo() *types.Info {
|
|
return pkg.typesInfo
|
|
}
|
|
|
|
func (pkg *Package) GetTypesSizes() types.Sizes {
|
|
return pkg.typesSizes
|
|
}
|
|
|
|
func (pkg *Package) IsIllTyped() bool {
|
|
return pkg.types == nil && pkg.typesInfo == nil
|
|
}
|
|
|
|
func (pkg *Package) GetImport(pkgPath string) source.Package {
|
|
imported := pkg.imports[pkgPath]
|
|
// Be careful not to return a nil pointer because that still satisfies the
|
|
// interface.
|
|
if imported != nil {
|
|
return imported
|
|
}
|
|
return nil
|
|
}
|