mirror of
https://github.com/golang/go
synced 2024-11-19 05:44:40 -07:00
8110780cfa
This change brings back handling for circular imports, which was removed because I originally thought that go/packages would handle that. However, since we are type-checking from source, we still end up having to deal with that. Additionally, we propagate the errors of type-checking to the diagnostics so that the user can actually see some of the problems. Change-Id: I0139bcaae461f1bcaf95706532bc5026f2430101 Reviewed-on: https://go-review.googlesource.com/c/tools/+/166882 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
124 lines
2.7 KiB
Go
124 lines
2.7 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
|
|
|
|
// 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 {
|
|
ready chan struct{}
|
|
*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.ready:
|
|
case <-ctx.Done():
|
|
return nil, ctx.Err()
|
|
}
|
|
} else {
|
|
// cache miss
|
|
e = &analysisEntry{
|
|
ready: make(chan struct{}),
|
|
Action: &source.Action{
|
|
Analyzer: a,
|
|
Pkg: pkg,
|
|
},
|
|
}
|
|
pkg.analyses[a] = e
|
|
pkg.mu.Unlock()
|
|
|
|
// 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 := pkg.imports[importPath]
|
|
act, err := dep.GetActionGraph(ctx, a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e.Deps = append(e.Deps, act)
|
|
}
|
|
}
|
|
close(e.ready)
|
|
}
|
|
return e.Action, nil
|
|
}
|
|
|
|
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) IsIllTyped() bool {
|
|
return pkg.types == nil && pkg.typesInfo == nil
|
|
}
|