go.tools/ssa: fix crash in SSA builder when using GCImporter to satisfy imports (ssadump -build=G).
Packages were not being created for all types.Packages, specifically, indirectly imported packages were missing. (*Program).CreatePackages now iterates over the type-checker's package map too. Also: removed all concurrency from importer. I think it was incorrect (and hard to fix). Also: change LoadInitialPackages so that all named packages are loaded from source. This happens regardless of whether GCImporter is used to satisfy imports. Details: - importer.Config.SourceImports flag determines whether to load all packages from *.go source. (Before, this was indicated by Config.Build != nil.) - importer.Config.Build field effectively defaults to &go/build.Default. A zero importer.Config is now usable. - importer.Importer.Config field is now exported. - LoadPackage renamed to ImportPackage since the resulting packages may come from GCImporter (and be incomplete). - doImport and ImportPackage fused. Fixes golang/go#7028 R=gri, axwalk CC=golang-codereviews https://golang.org/cl/48770043
This commit is contained in:
parent
df3357d07f
commit
3d82e7e94a
|
@ -73,7 +73,10 @@ func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
|
|
||||||
impctx := importer.Config{Build: &build.Default}
|
impctx := importer.Config{
|
||||||
|
Build: &build.Default,
|
||||||
|
SourceImports: true,
|
||||||
|
}
|
||||||
// TODO(adonovan): make go/types choose its default Sizes from
|
// TODO(adonovan): make go/types choose its default Sizes from
|
||||||
// build.Default or a specified *build.Context.
|
// build.Default or a specified *build.Context.
|
||||||
var wordSize int64 = 8
|
var wordSize int64 = 8
|
||||||
|
@ -103,7 +106,7 @@ func main() {
|
||||||
case 'N':
|
case 'N':
|
||||||
mode |= ssa.NaiveForm
|
mode |= ssa.NaiveForm
|
||||||
case 'G':
|
case 'G':
|
||||||
impctx.Build = nil
|
impctx.SourceImports = false
|
||||||
case 'L':
|
case 'L':
|
||||||
mode |= ssa.BuildSerially
|
mode |= ssa.BuildSerially
|
||||||
default:
|
default:
|
||||||
|
@ -147,8 +150,8 @@ func main() {
|
||||||
|
|
||||||
// The interpreter needs the runtime package.
|
// The interpreter needs the runtime package.
|
||||||
if *runFlag {
|
if *runFlag {
|
||||||
if _, err := imp.LoadPackage("runtime"); err != nil {
|
if _, err := imp.ImportPackage("runtime"); err != nil {
|
||||||
log.Fatalf("LoadPackage(runtime) failed: %s", err)
|
log.Fatalf("ImportPackage(runtime) failed: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,6 @@ import (
|
||||||
"go/token"
|
"go/token"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"code.google.com/p/go.tools/astutil"
|
"code.google.com/p/go.tools/astutil"
|
||||||
"code.google.com/p/go.tools/go/exact"
|
"code.google.com/p/go.tools/go/exact"
|
||||||
|
@ -60,32 +59,28 @@ import (
|
||||||
|
|
||||||
// An Importer's exported methods are not thread-safe.
|
// An Importer's exported methods are not thread-safe.
|
||||||
type Importer struct {
|
type Importer struct {
|
||||||
Fset *token.FileSet // position info for all files seen
|
Fset *token.FileSet // position info for all files seen
|
||||||
config *Config // the client configuration, unmodified
|
Config *Config // the client configuration, unmodified
|
||||||
importfn types.Importer // client's type import function
|
importfn types.Importer // client's type import function
|
||||||
augment map[string]bool // packages to be augmented by TestFiles when imported
|
srcpkgs map[string]bool // for each package to load from source, whether to augment
|
||||||
allPackagesMu sync.Mutex // guards 'allPackages' during internal concurrency
|
allPackages []*PackageInfo // all packages, including non-importable ones
|
||||||
allPackages []*PackageInfo // all packages, including non-importable ones
|
imported map[string]*importInfo // all imported packages (incl. failures) by import path
|
||||||
importedMu sync.Mutex // guards 'imported'
|
|
||||||
imported map[string]*importInfo // all imported packages (incl. failures) by import path
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// importInfo holds internal information about each import path.
|
// importInfo holds internal information about each import path.
|
||||||
type importInfo struct {
|
type importInfo struct {
|
||||||
path string // import path
|
path string // import path
|
||||||
info *PackageInfo // results of typechecking (including type errors)
|
info *PackageInfo // results of typechecking (including type errors)
|
||||||
err error // reason for failure to construct a package
|
err error // reason for failure to construct a package
|
||||||
ready chan struct{} // channel close is notification of ready state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config specifies the configuration for the importer.
|
// Config specifies the configuration for the importer.
|
||||||
|
// The zero value for Config is a ready-to-use default configuration.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// TypeChecker contains options relating to the type checker.
|
// TypeChecker contains options relating to the type checker.
|
||||||
//
|
//
|
||||||
// The supplied IgnoreFuncBodies is not used; the effective
|
// The supplied IgnoreFuncBodies is not used; the effective
|
||||||
// value comes from the TypeCheckFuncBodies func below.
|
// value comes from the TypeCheckFuncBodies func below.
|
||||||
//
|
|
||||||
// All callbacks must be thread-safe.
|
|
||||||
TypeChecker types.Config
|
TypeChecker types.Config
|
||||||
|
|
||||||
// TypeCheckFuncBodies is a predicate over package import
|
// TypeCheckFuncBodies is a predicate over package import
|
||||||
|
@ -94,23 +89,35 @@ type Config struct {
|
||||||
// its function bodies; this can be used to quickly load
|
// its function bodies; this can be used to quickly load
|
||||||
// dependencies from source. If nil, all func bodies are type
|
// dependencies from source. If nil, all func bodies are type
|
||||||
// checked.
|
// checked.
|
||||||
//
|
|
||||||
// Must be thread-safe.
|
|
||||||
//
|
|
||||||
TypeCheckFuncBodies func(string) bool
|
TypeCheckFuncBodies func(string) bool
|
||||||
|
|
||||||
// If Build is non-nil, it is used to satisfy imports.
|
// SourceImports determines whether to satisfy all imports by
|
||||||
|
// loading Go source code.
|
||||||
//
|
//
|
||||||
// If it is nil, binary object files produced by the gc
|
// If false, the TypeChecker.Import mechanism will be used
|
||||||
// compiler will be loaded instead of source code for all
|
// instead. Since that typically supplies only the types of
|
||||||
// imported packages. Such files supply only the types of
|
|
||||||
// package-level declarations and values of constants, but no
|
// package-level declarations and values of constants, but no
|
||||||
// code, so this mode will not yield a whole program. It is
|
// code, it will not yield a whole program. It is intended
|
||||||
// intended for analyses that perform intraprocedural analysis
|
// for analyses that perform intraprocedural analysis of a
|
||||||
// of a single package.
|
// single package.
|
||||||
|
//
|
||||||
|
// The importer's initial packages are always loaded from
|
||||||
|
// source, regardless of this flag's setting.
|
||||||
|
SourceImports bool
|
||||||
|
|
||||||
|
// If Build is non-nil, it is used to locate source packages.
|
||||||
|
// Otherwise &build.Default is used.
|
||||||
Build *build.Context
|
Build *build.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build returns the effective build context.
|
||||||
|
func (c *Config) build() *build.Context {
|
||||||
|
if c.Build != nil {
|
||||||
|
return c.Build
|
||||||
|
}
|
||||||
|
return &build.Default
|
||||||
|
}
|
||||||
|
|
||||||
// New returns a new, empty Importer using configuration options
|
// New returns a new, empty Importer using configuration options
|
||||||
// specified by config.
|
// specified by config.
|
||||||
//
|
//
|
||||||
|
@ -129,27 +136,27 @@ func New(config *Config) *Importer {
|
||||||
|
|
||||||
imp := &Importer{
|
imp := &Importer{
|
||||||
Fset: token.NewFileSet(),
|
Fset: token.NewFileSet(),
|
||||||
config: config,
|
Config: config,
|
||||||
importfn: importfn,
|
importfn: importfn,
|
||||||
augment: make(map[string]bool),
|
srcpkgs: make(map[string]bool),
|
||||||
imported: make(map[string]*importInfo),
|
imported: make(map[string]*importInfo),
|
||||||
}
|
}
|
||||||
return imp
|
return imp
|
||||||
}
|
}
|
||||||
|
|
||||||
// AllPackages returns a new slice containing all packages loaded by
|
// AllPackages returns a new slice containing all complete packages
|
||||||
// importer imp.
|
// loaded by importer imp.
|
||||||
|
//
|
||||||
|
// This returns only packages that were loaded from source or directly
|
||||||
|
// imported from a source package. It does not include packages
|
||||||
|
// indirectly referenced by a binary package; they are found in
|
||||||
|
// config.TypeChecker.Packages.
|
||||||
|
// TODO(adonovan): rethink this API.
|
||||||
//
|
//
|
||||||
func (imp *Importer) AllPackages() []*PackageInfo {
|
func (imp *Importer) AllPackages() []*PackageInfo {
|
||||||
return append([]*PackageInfo(nil), imp.allPackages...)
|
return append([]*PackageInfo(nil), imp.allPackages...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (imp *Importer) addPackage(info *PackageInfo) {
|
|
||||||
imp.allPackagesMu.Lock()
|
|
||||||
imp.allPackages = append(imp.allPackages, info)
|
|
||||||
imp.allPackagesMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// doImport imports the package denoted by path.
|
// doImport imports the package denoted by path.
|
||||||
// It implements the types.Importer prototype.
|
// It implements the types.Importer prototype.
|
||||||
//
|
//
|
||||||
|
@ -163,40 +170,7 @@ func (imp *Importer) addPackage(info *PackageInfo) {
|
||||||
// the types.Config.Error callback (the first of which is also saved
|
// the types.Config.Error callback (the first of which is also saved
|
||||||
// in the package's PackageInfo).
|
// in the package's PackageInfo).
|
||||||
//
|
//
|
||||||
// Idempotent and thread-safe.
|
// Idempotent.
|
||||||
//
|
|
||||||
//
|
|
||||||
// TODO(gri): The imports map (an alias for TypeChecker.Packages) must
|
|
||||||
// not be concurrently accessed. Today, the only (non-test) accesses
|
|
||||||
// of this map are in the client-supplied implementation of the
|
|
||||||
// importer function, i.e. the function below. But this is a fragile
|
|
||||||
// API because if go/types ever starts to access this map, it and its
|
|
||||||
// clients will have to agree to use the same mutex.
|
|
||||||
// Two better ideas:
|
|
||||||
//
|
|
||||||
// (1) require that the importer functions be stateful and have this
|
|
||||||
// map be part of that internal state.
|
|
||||||
// Pro: good encapsulation.
|
|
||||||
// Con: we can't have different importers collaborate, e.g.
|
|
||||||
// we can't use a source importer for some packages and
|
|
||||||
// GcImport for others since they'd each have a distinct map.
|
|
||||||
//
|
|
||||||
// (2) have there be a single map in go/types.Config, but expose
|
|
||||||
// lookup and update behind an interface and pass that interface
|
|
||||||
// to the importer implementations.
|
|
||||||
// Pro: sharing of the map among importers.
|
|
||||||
//
|
|
||||||
// This is idempotent but still doesn't address the issue of
|
|
||||||
// atomicity: when loading packages concurrently, we want to avoid
|
|
||||||
// the benign but suboptimal situation of two goroutines both
|
|
||||||
// importing "fmt", finding it not present, doing all the work, and
|
|
||||||
// double-updating the map.
|
|
||||||
// The interface to the map needs to express the idea that when a
|
|
||||||
// caller requests an import from the map and finds it not present,
|
|
||||||
// then it (and no other goroutine) becomes responsible for loading
|
|
||||||
// the package and updating the map; other goroutines should block
|
|
||||||
// until then. That's exactly what doImport0 below does; I think
|
|
||||||
// some of that logic should migrate into go/types.check.resolveFiles.
|
|
||||||
//
|
//
|
||||||
func (imp *Importer) doImport(imports map[string]*types.Package, path string) (*types.Package, error) {
|
func (imp *Importer) doImport(imports map[string]*types.Package, path string) (*types.Package, error) {
|
||||||
// Package unsafe is handled specially, and has no PackageInfo.
|
// Package unsafe is handled specially, and has no PackageInfo.
|
||||||
|
@ -205,99 +179,67 @@ func (imp *Importer) doImport(imports map[string]*types.Package, path string) (*
|
||||||
return types.Unsafe, nil
|
return types.Unsafe, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := imp.doImport0(imports, path)
|
info, err := imp.ImportPackage(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if imports != nil {
|
// Update the type checker's package map on success.
|
||||||
// Update the package's imports map, whether success or failure.
|
imports[path] = info.Pkg
|
||||||
//
|
|
||||||
// types.Package.Imports() is used by PackageInfo.Imports and
|
|
||||||
// thence by ssa.builder.
|
|
||||||
// TODO(gri): given that it doesn't specify whether it
|
|
||||||
// contains direct or transitive dependencies, it probably
|
|
||||||
// shouldn't be exposed. This package can make its own
|
|
||||||
// arrangements to implement PackageInfo.Imports().
|
|
||||||
importsMu.Lock()
|
|
||||||
imports[path] = info.Pkg
|
|
||||||
importsMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return info.Pkg, nil
|
return info.Pkg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var importsMu sync.Mutex // hack; see comment at doImport
|
// ImportPackage imports the package whose import path is path, plus
|
||||||
|
// its necessary dependencies.
|
||||||
// Like doImport, but returns a PackageInfo.
|
//
|
||||||
// Precondition: path != "unsafe".
|
// Precondition: path != "unsafe".
|
||||||
func (imp *Importer) doImport0(imports map[string]*types.Package, path string) (*PackageInfo, error) {
|
//
|
||||||
imp.importedMu.Lock()
|
func (imp *Importer) ImportPackage(path string) (*PackageInfo, error) {
|
||||||
ii, ok := imp.imported[path]
|
ii, ok := imp.imported[path]
|
||||||
if !ok {
|
if !ok {
|
||||||
ii = &importInfo{path: path, ready: make(chan struct{})}
|
ii = &importInfo{path: path}
|
||||||
imp.imported[path] = ii
|
imp.imported[path] = ii
|
||||||
}
|
|
||||||
imp.importedMu.Unlock()
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
// Find and create the actual package.
|
// Find and create the actual package.
|
||||||
if imp.config.Build != nil {
|
if augment, ok := imp.srcpkgs[ii.path]; ok || imp.Config.SourceImports {
|
||||||
imp.importSource(path, ii)
|
which := "g" // load *.go files
|
||||||
|
if augment {
|
||||||
|
which = "gt" // augment package by in-package *_test.go files
|
||||||
|
}
|
||||||
|
imp.loadFromSource(ii, which)
|
||||||
} else {
|
} else {
|
||||||
imp.importBinary(imports, ii)
|
imp.loadFromBinary(ii)
|
||||||
}
|
}
|
||||||
if ii.info != nil {
|
if ii.info != nil {
|
||||||
ii.info.Importable = true
|
ii.info.Importable = true
|
||||||
}
|
}
|
||||||
|
|
||||||
close(ii.ready) // enter ready state and wake up waiters
|
|
||||||
} else {
|
|
||||||
<-ii.ready // wait for ready condition
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invariant: ii is ready.
|
|
||||||
|
|
||||||
return ii.info, ii.err
|
return ii.info, ii.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// importBinary implements package loading from the client-supplied
|
// loadFromBinary implements package loading from the client-supplied
|
||||||
// external source, e.g. object files from the gc compiler.
|
// external source, e.g. object files from the gc compiler.
|
||||||
//
|
//
|
||||||
func (imp *Importer) importBinary(imports map[string]*types.Package, ii *importInfo) {
|
func (imp *Importer) loadFromBinary(ii *importInfo) {
|
||||||
pkg, err := imp.importfn(imports, ii.path)
|
pkg, err := imp.importfn(imp.Config.TypeChecker.Packages, ii.path)
|
||||||
if pkg != nil {
|
if pkg != nil {
|
||||||
ii.info = &PackageInfo{Pkg: pkg}
|
ii.info = &PackageInfo{Pkg: pkg}
|
||||||
imp.addPackage(ii.info)
|
imp.allPackages = append(imp.allPackages, ii.info)
|
||||||
} else {
|
} else {
|
||||||
ii.err = err
|
ii.err = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// importSource implements package loading by parsing Go source files
|
// loadFromSource implements package loading by parsing Go source files
|
||||||
// located by go/build.
|
// located by go/build. which indicates which files to include in the
|
||||||
|
// package.
|
||||||
//
|
//
|
||||||
func (imp *Importer) importSource(path string, ii *importInfo) {
|
func (imp *Importer) loadFromSource(ii *importInfo, which string) {
|
||||||
which := "g" // load *.go files
|
if files, err := parsePackageFiles(imp.Config.build(), imp.Fset, ii.path, which); err == nil {
|
||||||
if imp.augment[path] {
|
|
||||||
which = "gt" // augment package by in-package *_test.go files
|
|
||||||
}
|
|
||||||
if files, err := parsePackageFiles(imp.config.Build, imp.Fset, path, which); err == nil {
|
|
||||||
// Prefetch the imports asynchronously.
|
|
||||||
for path := range importsOf(path, files) {
|
|
||||||
go func(path string) { imp.doImport(nil, path) }(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type-check the package.
|
// Type-check the package.
|
||||||
ii.info = imp.CreatePackage(path, files...)
|
ii.info = imp.CreatePackage(ii.path, files...)
|
||||||
|
|
||||||
// We needn't wait for the prefetching goroutines to
|
|
||||||
// finish. Each one either runs quickly and populates
|
|
||||||
// the imported map, in which case the type checker
|
|
||||||
// will wait for the map entry to become ready; or, it
|
|
||||||
// runs slowly, even after we return, but then becomes
|
|
||||||
// just another map waiter, in which case it won't
|
|
||||||
// mutate anything.
|
|
||||||
} else {
|
} else {
|
||||||
ii.err = err
|
ii.err = err
|
||||||
}
|
}
|
||||||
|
@ -335,9 +277,9 @@ func (imp *Importer) CreatePackage(path string, files ...*ast.File) *PackageInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a copy of the types.Config so we can vary IgnoreFuncBodies.
|
// Use a copy of the types.Config so we can vary IgnoreFuncBodies.
|
||||||
tc := imp.config.TypeChecker
|
tc := imp.Config.TypeChecker
|
||||||
tc.IgnoreFuncBodies = false
|
tc.IgnoreFuncBodies = false
|
||||||
if f := imp.config.TypeCheckFuncBodies; f != nil {
|
if f := imp.Config.TypeCheckFuncBodies; f != nil {
|
||||||
tc.IgnoreFuncBodies = !f(path)
|
tc.IgnoreFuncBodies = !f(path)
|
||||||
}
|
}
|
||||||
if tc.Error == nil {
|
if tc.Error == nil {
|
||||||
|
@ -345,7 +287,7 @@ func (imp *Importer) CreatePackage(path string, files ...*ast.File) *PackageInfo
|
||||||
}
|
}
|
||||||
tc.Import = imp.doImport // doImport wraps the user's importfn, effectively
|
tc.Import = imp.doImport // doImport wraps the user's importfn, effectively
|
||||||
info.Pkg, info.Err = tc.Check(path, imp.Fset, files, &info.Info)
|
info.Pkg, info.Err = tc.Check(path, imp.Fset, files, &info.Info)
|
||||||
imp.addPackage(info)
|
imp.allPackages = append(imp.allPackages, info)
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,6 +360,7 @@ func (imp *Importer) LoadInitialPackages(args []string) ([]*PackageInfo, []strin
|
||||||
|
|
||||||
// Pass 1: parse the sets of files for each package.
|
// Pass 1: parse the sets of files for each package.
|
||||||
var pkgs []*initialPkg
|
var pkgs []*initialPkg
|
||||||
|
var seenAugmented bool
|
||||||
for len(args) > 0 {
|
for len(args) > 0 {
|
||||||
arg := args[0]
|
arg := args[0]
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
|
@ -444,28 +387,24 @@ func (imp *Importer) LoadInitialPackages(args []string) ([]*PackageInfo, []strin
|
||||||
continue // ignore; has no PackageInfo
|
continue // ignore; has no PackageInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg := &initialPkg{
|
pkgs = append(pkgs, &initialPkg{
|
||||||
path: path,
|
path: path,
|
||||||
importable: true,
|
importable: true,
|
||||||
}
|
})
|
||||||
pkgs = append(pkgs, pkg)
|
imp.srcpkgs[path] = false // unaugmented source package
|
||||||
|
|
||||||
if path != arg {
|
if path != arg {
|
||||||
continue // had "notest:" prefix
|
continue // had "notest:" prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
if imp.config.Build == nil {
|
|
||||||
continue // can't locate *_test.go files
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(adonovan): due to limitations of the current type
|
// TODO(adonovan): due to limitations of the current type
|
||||||
// checker design, we can augment at most one package.
|
// checker design, we can augment at most one package.
|
||||||
if len(imp.augment) > 0 {
|
if seenAugmented {
|
||||||
continue // don't attempt a second
|
continue // don't attempt a second
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the external test package.
|
// Load the external test package.
|
||||||
xtestFiles, err := parsePackageFiles(imp.config.Build, imp.Fset, path, "x")
|
xtestFiles, err := parsePackageFiles(imp.Config.build(), imp.Fset, path, "x")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -479,24 +418,22 @@ func (imp *Importer) LoadInitialPackages(args []string) ([]*PackageInfo, []strin
|
||||||
|
|
||||||
// Mark the non-xtest package for augmentation with
|
// Mark the non-xtest package for augmentation with
|
||||||
// in-package *_test.go files when we import it below.
|
// in-package *_test.go files when we import it below.
|
||||||
imp.augment[pkg.path] = true
|
imp.srcpkgs[path] = true
|
||||||
|
seenAugmented = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass 2: type-check each set of files to make a package.
|
// Pass 2: type-check each set of files to make a package.
|
||||||
var infos []*PackageInfo
|
var infos []*PackageInfo
|
||||||
imports := make(map[string]*types.Package) // keep importBinary happy
|
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
var info *PackageInfo
|
var info *PackageInfo
|
||||||
if pkg.importable {
|
if pkg.importable {
|
||||||
// import package
|
|
||||||
var err error
|
var err error
|
||||||
info, err = imp.doImport0(imports, pkg.path)
|
info, err = imp.ImportPackage(pkg.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err // e.g. parse error (but not type error)
|
return nil, nil, err // e.g. parse error (but not type error)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// create package
|
|
||||||
info = imp.CreatePackage(pkg.path, pkg.files...)
|
info = imp.CreatePackage(pkg.path, pkg.files...)
|
||||||
}
|
}
|
||||||
infos = append(infos, info)
|
infos = append(infos, info)
|
||||||
|
@ -509,17 +446,9 @@ func (imp *Importer) LoadInitialPackages(args []string) ([]*PackageInfo, []strin
|
||||||
return infos, args, nil
|
return infos, args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadPackage loads and type-checks the package whose import path is
|
|
||||||
// path, plus its necessary dependencies.
|
|
||||||
//
|
|
||||||
func (imp *Importer) LoadPackage(path string) (*PackageInfo, error) {
|
|
||||||
imports := make(map[string]*types.Package) // keep importBinary happy
|
|
||||||
return imp.doImport0(imports, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
type initialPkg struct {
|
type initialPkg struct {
|
||||||
path string // the package's import path
|
path string // the package's import path
|
||||||
importable bool // add package to import map false for main and xtests)
|
importable bool // add package to import map (false for main and xtests)
|
||||||
files []*ast.File // set of files (non-importable packages only)
|
files []*ast.File // set of files (non-importable packages only)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,15 @@ package importer_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.google.com/p/go.tools/importer"
|
"code.google.com/p/go.tools/importer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLoadInitialPackages(t *testing.T) {
|
func TestLoadInitialPackages(t *testing.T) {
|
||||||
ctxt := &importer.Config{Build: &build.Default}
|
|
||||||
|
|
||||||
// Failed load: bad first import path causes parsePackageFiles to fail.
|
// Failed load: bad first import path causes parsePackageFiles to fail.
|
||||||
args := []string{"nosuchpkg", "errors"}
|
args := []string{"nosuchpkg", "errors"}
|
||||||
if _, _, err := importer.New(ctxt).LoadInitialPackages(args); err == nil {
|
if _, _, err := importer.New(&importer.Config{}).LoadInitialPackages(args); err == nil {
|
||||||
t.Errorf("LoadInitialPackages(%q) succeeded, want failure", args)
|
t.Errorf("LoadInitialPackages(%q) succeeded, want failure", args)
|
||||||
} else {
|
} else {
|
||||||
// cannot find package: ok.
|
// cannot find package: ok.
|
||||||
|
@ -25,7 +22,7 @@ func TestLoadInitialPackages(t *testing.T) {
|
||||||
|
|
||||||
// Failed load: bad second import path proceeds to doImport0, which fails.
|
// Failed load: bad second import path proceeds to doImport0, which fails.
|
||||||
args = []string{"errors", "nosuchpkg"}
|
args = []string{"errors", "nosuchpkg"}
|
||||||
if _, _, err := importer.New(ctxt).LoadInitialPackages(args); err == nil {
|
if _, _, err := importer.New(&importer.Config{}).LoadInitialPackages(args); err == nil {
|
||||||
t.Errorf("LoadInitialPackages(%q) succeeded, want failure", args)
|
t.Errorf("LoadInitialPackages(%q) succeeded, want failure", args)
|
||||||
} else {
|
} else {
|
||||||
// cannot find package: ok
|
// cannot find package: ok
|
||||||
|
@ -33,7 +30,7 @@ func TestLoadInitialPackages(t *testing.T) {
|
||||||
|
|
||||||
// Successful load.
|
// Successful load.
|
||||||
args = []string{"fmt", "errors", "testdata/a.go,testdata/b.go", "--", "surplus"}
|
args = []string{"fmt", "errors", "testdata/a.go,testdata/b.go", "--", "surplus"}
|
||||||
imp := importer.New(ctxt)
|
imp := importer.New(&importer.Config{})
|
||||||
infos, rest, err := imp.LoadInitialPackages(args)
|
infos, rest, err := imp.LoadInitialPackages(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("LoadInitialPackages(%q) failed: %s", args, err)
|
t.Errorf("LoadInitialPackages(%q) failed: %s", args, err)
|
||||||
|
|
|
@ -82,7 +82,7 @@ func TestEnclosingFunction(t *testing.T) {
|
||||||
"900", "func@2.27"},
|
"900", "func@2.27"},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
imp := importer.New(new(importer.Config)) // (NB: no go/build.Config)
|
imp := importer.New(&importer.Config{})
|
||||||
f, start, end := findInterval(t, imp.Fset, test.input, test.substr)
|
f, start, end := findInterval(t, imp.Fset, test.input, test.substr)
|
||||||
if f == nil {
|
if f == nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -192,7 +192,7 @@ func (res *Result) Serial() *serial.Result {
|
||||||
// Clients that intend to perform multiple queries against the same
|
// Clients that intend to perform multiple queries against the same
|
||||||
// analysis scope should use this pattern instead:
|
// analysis scope should use this pattern instead:
|
||||||
//
|
//
|
||||||
// imp := importer.New(&importer.Config{Build: buildContext})
|
// imp := importer.New(&importer.Config{Build: buildContext, SourceImports: true})
|
||||||
// o, err := oracle.New(imp, args, nil)
|
// o, err := oracle.New(imp, args, nil)
|
||||||
// if err != nil { ... }
|
// if err != nil { ... }
|
||||||
// for ... {
|
// for ... {
|
||||||
|
@ -219,7 +219,7 @@ func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *buil
|
||||||
return nil, fmt.Errorf("invalid mode type: %q", mode)
|
return nil, fmt.Errorf("invalid mode type: %q", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
impcfg := importer.Config{Build: buildContext}
|
impcfg := importer.Config{Build: buildContext, SourceImports: true}
|
||||||
|
|
||||||
// For queries needing only a single typed package,
|
// For queries needing only a single typed package,
|
||||||
// reduce the analysis scope to that package.
|
// reduce the analysis scope to that package.
|
||||||
|
|
|
@ -6,7 +6,6 @@ package pointer_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
@ -41,8 +40,7 @@ func main() {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
// Construct an importer.
|
// Construct an importer.
|
||||||
// Imports will be loaded as if by 'go build'.
|
imp := importer.New(&importer.Config{SourceImports: true})
|
||||||
imp := importer.New(&importer.Config{Build: &build.Default})
|
|
||||||
|
|
||||||
// Parse the input file.
|
// Parse the input file.
|
||||||
file, err := parser.ParseFile(imp.Fset, "myprog.go", myprog, 0)
|
file, err := parser.ParseFile(imp.Fset, "myprog.go", myprog, 0)
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -152,7 +151,7 @@ func findProbe(prog *ssa.Program, probes map[*ssa.CallCommon]pointer.Pointer, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func doOneInput(input, filename string) bool {
|
func doOneInput(input, filename string) bool {
|
||||||
impctx := &importer.Config{Build: &build.Default}
|
impctx := &importer.Config{SourceImports: true}
|
||||||
imp := importer.New(impctx)
|
imp := importer.New(impctx)
|
||||||
|
|
||||||
// Parsing.
|
// Parsing.
|
||||||
|
|
|
@ -810,9 +810,6 @@ func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
|
||||||
c.Method = obj
|
c.Method = obj
|
||||||
} else {
|
} else {
|
||||||
// "Call"-mode call.
|
// "Call"-mode call.
|
||||||
// TODO(adonovan): fix: in -build=G
|
|
||||||
// mode, declaredFunc panics for
|
|
||||||
// cross-package calls.
|
|
||||||
c.Value = fn.Prog.declaredFunc(obj)
|
c.Value = fn.Prog.declaredFunc(obj)
|
||||||
c.Args = append(c.Args, v)
|
c.Args = append(c.Args, v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ func main() {
|
||||||
w.Write(nil) // interface invoke of external declared method
|
w.Write(nil) // interface invoke of external declared method
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
imp := importer.New(new(importer.Config)) // no go/build.Context; uses GC importer
|
imp := importer.New(&importer.Config{})
|
||||||
|
|
||||||
f, err := parser.ParseFile(imp.Fset, "<input>", test, 0)
|
f, err := parser.ParseFile(imp.Fset, "<input>", test, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -58,10 +58,16 @@ func main() {
|
||||||
mainPkg := prog.Package(mainInfo.Pkg)
|
mainPkg := prog.Package(mainInfo.Pkg)
|
||||||
mainPkg.Build()
|
mainPkg.Build()
|
||||||
|
|
||||||
// Only the main package and its immediate dependencies are loaded.
|
// The main package, its direct and indirect dependencies are loaded.
|
||||||
deps := []string{"bytes", "io", "testing"}
|
deps := []string{
|
||||||
|
// directly imported dependencies:
|
||||||
|
"bytes", "io", "testing",
|
||||||
|
// indirect dependencies (partial list):
|
||||||
|
"errors", "fmt", "os", "runtime",
|
||||||
|
}
|
||||||
|
|
||||||
all := prog.AllPackages()
|
all := prog.AllPackages()
|
||||||
if len(all) != 1+len(deps) {
|
if len(all) <= len(deps) {
|
||||||
t.Errorf("unexpected set of loaded packages: %q", all)
|
t.Errorf("unexpected set of loaded packages: %q", all)
|
||||||
}
|
}
|
||||||
for _, path := range deps {
|
for _, path := range deps {
|
||||||
|
@ -198,7 +204,7 @@ func TestTypesWithMethodSets(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
imp := importer.New(new(importer.Config)) // no go/build.Context; uses GC importer
|
imp := importer.New(&importer.Config{})
|
||||||
|
|
||||||
f, err := parser.ParseFile(imp.Fset, "<input>", test.input, 0)
|
f, err := parser.ParseFile(imp.Fset, "<input>", test.input, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -250,6 +250,8 @@ func (prog *Program) CreatePackage(info *importer.PackageInfo) *Package {
|
||||||
//
|
//
|
||||||
func (prog *Program) CreatePackages(imp *importer.Importer) error {
|
func (prog *Program) CreatePackages(imp *importer.Importer) error {
|
||||||
var errpkgs []string
|
var errpkgs []string
|
||||||
|
|
||||||
|
// Create source packages and directly imported packages.
|
||||||
for _, info := range imp.AllPackages() {
|
for _, info := range imp.AllPackages() {
|
||||||
if info.Err != nil {
|
if info.Err != nil {
|
||||||
errpkgs = append(errpkgs, info.Pkg.Path())
|
errpkgs = append(errpkgs, info.Pkg.Path())
|
||||||
|
@ -257,6 +259,15 @@ func (prog *Program) CreatePackages(imp *importer.Importer) error {
|
||||||
prog.CreatePackage(info)
|
prog.CreatePackage(info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create indirectly imported packages.
|
||||||
|
for _, obj := range imp.Config.TypeChecker.Packages {
|
||||||
|
prog.CreatePackage(&importer.PackageInfo{
|
||||||
|
Pkg: obj,
|
||||||
|
Importable: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if errpkgs != nil {
|
if errpkgs != nil {
|
||||||
return fmt.Errorf("couldn't create these SSA packages due to type errors: %s",
|
return fmt.Errorf("couldn't create these SSA packages due to type errors: %s",
|
||||||
strings.Join(errpkgs, ", "))
|
strings.Join(errpkgs, ", "))
|
||||||
|
|
|
@ -6,7 +6,6 @@ package ssa_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -42,8 +41,8 @@ func main() {
|
||||||
fmt.Println(message)
|
fmt.Println(message)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
// Construct an importer. Imports will be loaded as if by 'go build'.
|
// Construct an importer.
|
||||||
imp := importer.New(&importer.Config{Build: &build.Default})
|
imp := importer.New(&importer.Config{})
|
||||||
|
|
||||||
// Parse the input file.
|
// Parse the input file.
|
||||||
file, err := parser.ParseFile(imp.Fset, "hello.go", hello, 0)
|
file, err := parser.ParseFile(imp.Fset, "hello.go", hello, 0)
|
||||||
|
|
|
@ -168,7 +168,7 @@ func run(t *testing.T, dir, input string, success successPredicate) bool {
|
||||||
inputs = append(inputs, dir+i)
|
inputs = append(inputs, dir+i)
|
||||||
}
|
}
|
||||||
|
|
||||||
imp := importer.New(&importer.Config{Build: &build.Default})
|
imp := importer.New(&importer.Config{SourceImports: true})
|
||||||
// TODO(adonovan): use LoadInitialPackages, then un-export ParseFiles.
|
// TODO(adonovan): use LoadInitialPackages, then un-export ParseFiles.
|
||||||
// Then add the following packages' tests, which pass:
|
// Then add the following packages' tests, which pass:
|
||||||
// "flag", "unicode", "unicode/utf8", "testing", "log", "path".
|
// "flag", "unicode", "unicode/utf8", "testing", "log", "path".
|
||||||
|
@ -194,8 +194,8 @@ func run(t *testing.T, dir, input string, success successPredicate) bool {
|
||||||
hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build code.google.com/p/go.tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input)
|
hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build code.google.com/p/go.tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input)
|
||||||
mainInfo := imp.CreatePackage(files[0].Name.Name, files...)
|
mainInfo := imp.CreatePackage(files[0].Name.Name, files...)
|
||||||
|
|
||||||
if _, err := imp.LoadPackage("runtime"); err != nil {
|
if _, err := imp.ImportPackage("runtime"); err != nil {
|
||||||
t.Errorf("LoadPackage(runtime) failed: %s", err)
|
t.Errorf("ImportPackage(runtime) failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
|
prog := ssa.NewProgram(imp.Fset, ssa.SanityCheckFunctions)
|
||||||
|
@ -321,7 +321,7 @@ func TestTestmainPackage(t *testing.T) {
|
||||||
|
|
||||||
// CreateTestMainPackage should return nil if there were no tests.
|
// CreateTestMainPackage should return nil if there were no tests.
|
||||||
func TestNullTestmainPackage(t *testing.T) {
|
func TestNullTestmainPackage(t *testing.T) {
|
||||||
imp := importer.New(&importer.Config{Build: &build.Default})
|
imp := importer.New(&importer.Config{})
|
||||||
files, err := importer.ParseFiles(imp.Fset, ".", "testdata/b_test.go")
|
files, err := importer.ParseFiles(imp.Fset, ".", "testdata/b_test.go")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ParseFiles failed: %s", err)
|
t.Fatalf("ParseFiles failed: %s", err)
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestObjValueLookup(t *testing.T) {
|
func TestObjValueLookup(t *testing.T) {
|
||||||
imp := importer.New(new(importer.Config)) // (uses GCImporter)
|
imp := importer.New(&importer.Config{})
|
||||||
f, err := parser.ParseFile(imp.Fset, "testdata/objlookup.go", nil, parser.ParseComments)
|
f, err := parser.ParseFile(imp.Fset, "testdata/objlookup.go", nil, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -186,7 +186,7 @@ func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.
|
||||||
// Ensure that, in debug mode, we can determine the ssa.Value
|
// Ensure that, in debug mode, we can determine the ssa.Value
|
||||||
// corresponding to every ast.Expr.
|
// corresponding to every ast.Expr.
|
||||||
func TestValueForExpr(t *testing.T) {
|
func TestValueForExpr(t *testing.T) {
|
||||||
imp := importer.New(new(importer.Config)) // (uses GCImporter)
|
imp := importer.New(&importer.Config{})
|
||||||
f, err := parser.ParseFile(imp.Fset, "testdata/valueforexpr.go", nil, parser.ParseComments)
|
f, err := parser.ParseFile(imp.Fset, "testdata/valueforexpr.go", nil, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSwitches(t *testing.T) {
|
func TestSwitches(t *testing.T) {
|
||||||
imp := importer.New(new(importer.Config)) // (uses GCImporter)
|
imp := importer.New(&importer.Config{})
|
||||||
f, err := parser.ParseFile(imp.Fset, "testdata/switches.go", nil, parser.ParseComments)
|
f, err := parser.ParseFile(imp.Fset, "testdata/switches.go", nil, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
|
@ -10,7 +10,6 @@ package ssa_test
|
||||||
// Run test with GOMAXPROCS=8.
|
// Run test with GOMAXPROCS=8.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/build"
|
|
||||||
"go/token"
|
"go/token"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -52,12 +51,10 @@ func allPackages() []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStdlib(t *testing.T) {
|
func TestStdlib(t *testing.T) {
|
||||||
impctx := importer.Config{Build: &build.Default}
|
|
||||||
|
|
||||||
// Load, parse and type-check the program.
|
// Load, parse and type-check the program.
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
|
|
||||||
imp := importer.New(&impctx)
|
imp := importer.New(&importer.Config{})
|
||||||
|
|
||||||
if _, _, err := imp.LoadInitialPackages(allPackages()); err != nil {
|
if _, _, err := imp.LoadInitialPackages(allPackages()); err != nil {
|
||||||
t.Errorf("LoadInitialPackages failed: %s", err)
|
t.Errorf("LoadInitialPackages failed: %s", err)
|
||||||
|
|
Loading…
Reference in New Issue