go/packages: changes to the API per CL 125305
Collapse the main entry points to the single Load function. Make public the Mode enumeration in the Config. Add a lowest level mode that does not do the import graph. Remove PkgPath. Remove the DisableCgo option. Rename some fields of Package: Srcs -> GoFiles OtherSrcs -> OtherFiles Type -> Types Files -> Syntax Info -> TypesInfo delete the All function Change-Id: I54e5dc3ca5cb0b7e33fe6aa6f70cc66e18ea8cac Reviewed-on: https://go-review.googlesource.com/125535 Run-TryBot: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Russ Cox <rsc@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
parent
ded554d068
commit
214274eeeb
|
@ -31,7 +31,7 @@ type GoTooOldError struct {
|
|||
// golistPackages uses the "go list" command to expand the
|
||||
// pattern words and return metadata for the specified packages.
|
||||
// dir may be "" and env may be nil, as per os/exec.Command.
|
||||
func golistPackages(ctx context.Context, dir string, env []string, cgo, export, tests bool, words []string) ([]*loaderPackage, error) {
|
||||
func golistPackages(ctx context.Context, dir string, env []string, export, tests, deps bool, words []string) ([]*loaderPackage, error) {
|
||||
// Fields must match go list;
|
||||
// see $GOROOT/src/cmd/go/internal/load/pkg.go.
|
||||
type jsonPackage struct {
|
||||
|
@ -68,7 +68,7 @@ func golistPackages(ctx context.Context, dir string, env []string, cgo, export,
|
|||
// Run "go list" for complete
|
||||
// information on the specified packages.
|
||||
|
||||
buf, err := golist(ctx, dir, env, cgo, export, tests, words)
|
||||
buf, err := golist(ctx, dir, env, export, tests, deps, words)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ func golistPackages(ctx context.Context, dir string, env []string, cgo, export,
|
|||
}
|
||||
for id := range ids {
|
||||
// Go issue 26136: go list omits imports in cgo-generated files.
|
||||
if id == "C" && cgo {
|
||||
if id == "C" {
|
||||
imports["unsafe"] = "unsafe"
|
||||
imports["syscall"] = "syscall"
|
||||
if pkgpath != "runtime/cgo" {
|
||||
|
@ -156,10 +156,10 @@ func golistPackages(ctx context.Context, dir string, env []string, cgo, export,
|
|||
Package: &Package{
|
||||
ID: id,
|
||||
Name: p.Name,
|
||||
PkgPath: pkgpath,
|
||||
Srcs: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
|
||||
OtherSrcs: absJoin(p.Dir, p.SFiles, p.CFiles),
|
||||
GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
|
||||
OtherFiles: absJoin(p.Dir, p.SFiles, p.CFiles),
|
||||
},
|
||||
pkgpath: pkgpath,
|
||||
imports: imports,
|
||||
export: export,
|
||||
indirect: p.DepOnly,
|
||||
|
@ -184,15 +184,15 @@ func absJoin(dir string, fileses ...[]string) (res []string) {
|
|||
}
|
||||
|
||||
// golist returns the JSON-encoded result of a "go list args..." query.
|
||||
func golist(ctx context.Context, dir string, env []string, cgo, export, tests bool, args []string) (*bytes.Buffer, error) {
|
||||
func golist(ctx context.Context, dir string, env []string, export, tests, deps bool, args []string) (*bytes.Buffer, error) {
|
||||
out := new(bytes.Buffer)
|
||||
cmd := exec.CommandContext(ctx, "go", append([]string{
|
||||
"list",
|
||||
"-e",
|
||||
fmt.Sprintf("-cgo=%t", cgo),
|
||||
fmt.Sprintf("-cgo=%t", true),
|
||||
fmt.Sprintf("-test=%t", tests),
|
||||
fmt.Sprintf("-export=%t", export),
|
||||
"-deps",
|
||||
fmt.Sprintf("-deps=%t", deps),
|
||||
"-json",
|
||||
"--",
|
||||
}, args...)...)
|
||||
|
@ -200,9 +200,6 @@ func golist(ctx context.Context, dir string, env []string, cgo, export, tests bo
|
|||
if env == nil {
|
||||
env = os.Environ()
|
||||
}
|
||||
if !cgo {
|
||||
env = append(env, "CGO_ENABLED=0")
|
||||
}
|
||||
cmd.Env = env
|
||||
cmd.Dir = dir
|
||||
cmd.Stdout = out
|
||||
|
|
|
@ -28,8 +28,7 @@ import (
|
|||
var (
|
||||
depsFlag = flag.Bool("deps", false, "show dependencies too")
|
||||
testFlag = flag.Bool("test", false, "include any tests implied by the patterns")
|
||||
cgoFlag = flag.Bool("cgo", true, "process cgo files")
|
||||
mode = flag.String("mode", "metadata", "mode (one of metadata, typecheck, wholeprogram)")
|
||||
mode = flag.String("mode", "graph", "mode (one of metadata, graph, typed, alltyped)")
|
||||
private = flag.Bool("private", false, "show non-exported declarations too")
|
||||
|
||||
cpuprofile = flag.String("cpuprofile", "", "write CPU profile to this file")
|
||||
|
@ -103,26 +102,30 @@ func main() {
|
|||
}()
|
||||
}
|
||||
|
||||
// Load, parse, and type-check the packages named on the command line.
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadSyntax,
|
||||
Error: func(error) {}, // we'll take responsibility for printing errors
|
||||
Tests: *testFlag,
|
||||
}
|
||||
|
||||
// -mode flag
|
||||
load := packages.TypeCheck
|
||||
switch strings.ToLower(*mode) {
|
||||
case "metadata":
|
||||
load = packages.Metadata
|
||||
case "typecheck":
|
||||
load = packages.TypeCheck
|
||||
case "wholeprogram":
|
||||
load = packages.WholeProgram
|
||||
case "files":
|
||||
cfg.Mode = packages.LoadFiles
|
||||
case "imports":
|
||||
cfg.Mode = packages.LoadImports
|
||||
case "types":
|
||||
cfg.Mode = packages.LoadTypes
|
||||
case "syntax":
|
||||
cfg.Mode = packages.LoadSyntax
|
||||
case "allsyntax":
|
||||
cfg.Mode = packages.LoadAllSyntax
|
||||
default:
|
||||
log.Fatalf("invalid mode: %s", *mode)
|
||||
}
|
||||
|
||||
// Load, parse, and type-check the packages named on the command line.
|
||||
opts := &packages.Options{
|
||||
Error: func(error) {}, // we'll take responsibility for printing errors
|
||||
DisableCgo: !*cgoFlag,
|
||||
Tests: *testFlag,
|
||||
}
|
||||
lpkgs, err := load(opts, flag.Args()...)
|
||||
lpkgs, err := packages.Load(cfg, flag.Args()...)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -174,24 +177,23 @@ func print(lpkg *packages.Package) {
|
|||
}
|
||||
fmt.Printf("Go %s %q:\n", kind, lpkg.ID) // unique ID
|
||||
fmt.Printf("\tpackage %s\n", lpkg.Name)
|
||||
fmt.Printf("\treflect.Type.PkgPath %q\n", lpkg.PkgPath)
|
||||
|
||||
// characterize type info
|
||||
if lpkg.Type == nil {
|
||||
if lpkg.Types == nil {
|
||||
fmt.Printf("\thas no exported type info\n")
|
||||
} else if !lpkg.Type.Complete() {
|
||||
} else if !lpkg.Types.Complete() {
|
||||
fmt.Printf("\thas incomplete exported type info\n")
|
||||
} else if len(lpkg.Files) == 0 {
|
||||
} else if len(lpkg.Syntax) == 0 {
|
||||
fmt.Printf("\thas complete exported type info\n")
|
||||
} else {
|
||||
fmt.Printf("\thas complete exported type info and typed ASTs\n")
|
||||
}
|
||||
if lpkg.Type != nil && lpkg.IllTyped && len(lpkg.Errors) == 0 {
|
||||
if lpkg.Types != nil && lpkg.IllTyped && len(lpkg.Errors) == 0 {
|
||||
fmt.Printf("\thas an error among its dependencies\n")
|
||||
}
|
||||
|
||||
// source files
|
||||
for _, src := range lpkg.Srcs {
|
||||
for _, src := range lpkg.GoFiles {
|
||||
fmt.Printf("\tfile %s\n", src)
|
||||
}
|
||||
|
||||
|
@ -217,9 +219,9 @@ func print(lpkg *packages.Package) {
|
|||
}
|
||||
|
||||
// package members (TypeCheck or WholeProgram mode)
|
||||
if lpkg.Type != nil {
|
||||
qual := types.RelativeTo(lpkg.Type)
|
||||
scope := lpkg.Type.Scope()
|
||||
if lpkg.Types != nil {
|
||||
qual := types.RelativeTo(lpkg.Types)
|
||||
scope := lpkg.Types.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
obj := scope.Lookup(name)
|
||||
if !obj.Exported() && !*private {
|
||||
|
|
|
@ -20,10 +20,41 @@ import (
|
|||
"golang.org/x/tools/go/gcexportdata"
|
||||
)
|
||||
|
||||
// An Options specifies details about how packages should be loaded.
|
||||
// The loaders do not modify this struct.
|
||||
// TODO(rsc): Better name would be Config.
|
||||
type Options struct {
|
||||
// A LoadMode specifies the amount of detail to return when loading packages.
|
||||
type LoadMode int
|
||||
|
||||
const (
|
||||
_ LoadMode = iota
|
||||
|
||||
// LoadFiles finds the packages and computes their source file lists.
|
||||
// Package fields: ID, Name, Errors, GoFiles, OtherFiles.
|
||||
LoadFiles
|
||||
|
||||
// LoadImports adds import information for each package
|
||||
// and its dependencies.
|
||||
// Package fields added: Imports.
|
||||
LoadImports
|
||||
|
||||
// LoadTypes adds type information for the package's exported symbols.
|
||||
// Package fields added: Types, IllTyped.
|
||||
LoadTypes
|
||||
|
||||
// LoadSyntax adds typed syntax trees for the packages matching the patterns.
|
||||
// Package fields added: Syntax, TypesInfo, Fset, for direct pattern matches only.
|
||||
LoadSyntax
|
||||
|
||||
// LoadAllSyntax adds typed syntax trees for the packages matching the patterns
|
||||
// and all dependencies.
|
||||
// Package fields added: Syntax, TypesInfo, Fset, for all packages in import graph.
|
||||
LoadAllSyntax
|
||||
)
|
||||
|
||||
// An Config specifies details about how packages should be loaded.
|
||||
// Calls to Load do not modify this struct.
|
||||
type Config struct {
|
||||
// Mode controls the level of information returned for each package.
|
||||
Mode LoadMode
|
||||
|
||||
// Context specifies the context for the load operation.
|
||||
// If the context is cancelled, the loader may stop early
|
||||
// and return an ErrCancelled error.
|
||||
|
@ -35,14 +66,6 @@ type Options struct {
|
|||
// If Dir is empty, the tool is run in the current directory.
|
||||
Dir string
|
||||
|
||||
// DisableCgo disables cgo-processing of files that import "C",
|
||||
// and removes the 'cgo' build tag, which may affect source file selection.
|
||||
// By default, TypeCheck, and WholeProgram queries process such
|
||||
// files, and the resulting Package.Srcs describes the generated
|
||||
// files seen by the compiler.
|
||||
// TODO(rsc): Drop entirely. I don't think these are the right semantics.
|
||||
DisableCgo bool
|
||||
|
||||
// Env is the environment to use when invoking the build system tool.
|
||||
// If Env is nil, the current environment is used.
|
||||
// Like in os/exec's Cmd, only the last value in the slice for
|
||||
|
@ -110,37 +133,11 @@ type Options struct {
|
|||
TypeChecker types.Config
|
||||
}
|
||||
|
||||
// Metadata loads and returns the Go packages named by the given patterns,
|
||||
// omitting type information and type-checked syntax trees from all packages.
|
||||
// TODO(rsc): Better name would be Load.
|
||||
func Metadata(o *Options, patterns ...string) ([]*Package, error) {
|
||||
l := &loader{mode: metadata}
|
||||
if o != nil {
|
||||
l.Options = *o
|
||||
}
|
||||
return l.load(patterns...)
|
||||
}
|
||||
|
||||
// TypeCheck loads and returns the Go packages named by the given patterns.
|
||||
// It includes type information in all packages, including dependencies.
|
||||
// The packages named by the patterns also have type-checked syntax trees.
|
||||
// TODO(rsc): Better name would be LoadTyped.
|
||||
func TypeCheck(o *Options, patterns ...string) ([]*Package, error) {
|
||||
l := &loader{mode: typeCheck}
|
||||
if o != nil {
|
||||
l.Options = *o
|
||||
}
|
||||
return l.load(patterns...)
|
||||
}
|
||||
|
||||
// WholeProgram loads and returns the Go packages named by the given patterns.
|
||||
// It includes type information and type-checked syntax trees for all packages,
|
||||
// including dependencies.
|
||||
// TODO(rsc): Better name would be LoadAllTyped.
|
||||
func WholeProgram(o *Options, patterns ...string) ([]*Package, error) {
|
||||
l := &loader{mode: wholeProgram}
|
||||
if o != nil {
|
||||
l.Options = *o
|
||||
// Load and returns the Go packages named by the given patterns.
|
||||
func Load(cfg *Config, patterns ...string) ([]*Package, error) {
|
||||
l := &loader{}
|
||||
if cfg != nil {
|
||||
l.Config = *cfg
|
||||
}
|
||||
return l.load(patterns...)
|
||||
}
|
||||
|
@ -155,18 +152,6 @@ type Package struct {
|
|||
// interpret them.
|
||||
ID string
|
||||
|
||||
// PkgPath is the import path of the package during a particular build.
|
||||
//
|
||||
// Analyses that need a unique string to identify a returned Package
|
||||
// should use ID, not PkgPath. Although PkgPath does uniquely identify
|
||||
// a package in a particular build, the loader may return packages
|
||||
// spanning multiple builds (for example, multiple commands,
|
||||
// or a package and its tests), so PkgPath is not guaranteed unique
|
||||
// across all packages returned by a single load.
|
||||
//
|
||||
// TODO(rsc): This name should be ImportPath.
|
||||
PkgPath string
|
||||
|
||||
// Name is the package name as it appears in the package source code.
|
||||
Name string
|
||||
|
||||
|
@ -179,7 +164,7 @@ type Package struct {
|
|||
// to corresponding loaded Packages.
|
||||
Imports map[string]*Package
|
||||
|
||||
// Srcs lists the absolute file paths of the package's Go source files.
|
||||
// GoFiles lists the absolute file paths of the package's Go source files.
|
||||
//
|
||||
// If a package has typed syntax trees and the DisableCgo option is false,
|
||||
// the cgo-processed output files are listed instead of the original
|
||||
|
@ -192,20 +177,15 @@ type Package struct {
|
|||
// TODO(rsc): Actually, in TypeCheck mode even the packages without
|
||||
// syntax trees (pure dependencies) lose their original sources.
|
||||
// We should fix that.
|
||||
//
|
||||
// TODO(rsc): This should be GoFiles.
|
||||
Srcs []string
|
||||
GoFiles []string
|
||||
|
||||
// OtherSrcs lists the absolute file paths of the package's non-Go source files,
|
||||
// OtherFiles lists the absolute file paths of the package's non-Go source files,
|
||||
// including assembly, C, C++, Fortran, Objective-C, SWIG, and so on.
|
||||
//
|
||||
// TODO(rsc): This should be OtherFiles.
|
||||
OtherSrcs []string
|
||||
OtherFiles []string
|
||||
|
||||
// Type is the type information for the package.
|
||||
// The TypeCheck and WholeProgram loaders set this field for all packages.
|
||||
// TODO(rsc): This should be Types.
|
||||
Type *types.Package
|
||||
Types *types.Package
|
||||
|
||||
// IllTyped indicates whether the package has any type errors.
|
||||
// The TypeCheck and WholeProgram loaders set this field for all packages.
|
||||
|
@ -215,15 +195,11 @@ type Package struct {
|
|||
//
|
||||
// The TypeCheck loader sets Files for packages matching the patterns.
|
||||
// The WholeProgram loader sets Files for all packages, including dependencies.
|
||||
//
|
||||
// TODO(rsc): This should be ASTs or Syntax.
|
||||
Files []*ast.File
|
||||
Syntax []*ast.File
|
||||
|
||||
// Info is the type-checking results for the package's syntax trees.
|
||||
// It is set only when Files is set.
|
||||
//
|
||||
// TODO(rsc): This should be TypesInfo.
|
||||
Info *types.Info
|
||||
TypesInfo *types.Info
|
||||
|
||||
// Fset is the token.FileSet for the package's syntax trees listed in Files.
|
||||
// It is set only when Files is set.
|
||||
|
@ -249,42 +225,27 @@ type loaderPackage struct {
|
|||
loadOnce sync.Once
|
||||
color uint8 // for cycle detection
|
||||
mark, needsrc bool // used in TypeCheck mode only
|
||||
pkgpath string
|
||||
}
|
||||
|
||||
func (lpkg *Package) String() string { return lpkg.ID }
|
||||
|
||||
// loader holds the working state of a single call to load.
|
||||
type loader struct {
|
||||
mode mode
|
||||
cgo bool
|
||||
pkgs map[string]*loaderPackage
|
||||
Options
|
||||
Config
|
||||
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
|
||||
}
|
||||
|
||||
// The mode determines which packages are visited
|
||||
// and the level of information reported about each one.
|
||||
// Modes are ordered by increasing detail.
|
||||
type mode uint8
|
||||
|
||||
const (
|
||||
metadata = iota
|
||||
typeCheck
|
||||
wholeProgram
|
||||
)
|
||||
|
||||
func (ld *loader) load(patterns ...string) ([]*Package, error) {
|
||||
if ld.Context == nil {
|
||||
ld.Context = context.Background()
|
||||
}
|
||||
|
||||
if ld.mode > metadata {
|
||||
if ld.Mode >= LoadSyntax {
|
||||
if ld.Fset == nil {
|
||||
ld.Fset = token.NewFileSet()
|
||||
}
|
||||
|
||||
ld.cgo = !ld.DisableCgo
|
||||
|
||||
if ld.Error == nil {
|
||||
ld.Error = func(e error) {
|
||||
fmt.Fprintln(os.Stderr, e)
|
||||
|
@ -305,7 +266,9 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
|
|||
|
||||
// Do the metadata query and partial build.
|
||||
// TODO(adonovan): support alternative build systems at this seam.
|
||||
list, err := golistPackages(ld.Context, ld.Dir, ld.Env, ld.cgo, ld.mode == typeCheck, ld.Tests, patterns)
|
||||
export := ld.Mode > LoadImports && ld.Mode < LoadAllSyntax
|
||||
deps := ld.Mode >= LoadImports
|
||||
list, err := golistPackages(ld.Context, ld.Dir, ld.Env, export, ld.Tests, deps, patterns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -319,10 +282,13 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
|
|||
if !pkg.indirect {
|
||||
initial = append(initial, pkg)
|
||||
|
||||
if ld.mode == typeCheck {
|
||||
if ld.Mode == LoadSyntax {
|
||||
pkg.needsrc = true
|
||||
}
|
||||
}
|
||||
if ld.Mode >= LoadAllSyntax {
|
||||
pkg.needsrc = true
|
||||
}
|
||||
}
|
||||
if len(ld.pkgs) == 0 {
|
||||
return nil, fmt.Errorf("packages not found")
|
||||
|
@ -343,9 +309,9 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
|
|||
// Invalid imports (cycles and missing nodes) are saved in the importErrors map.
|
||||
// Thus, even in the presence of both kinds of errors, the Import graph remains a DAG.
|
||||
//
|
||||
// visit returns whether the package is initial or has a transitive
|
||||
// dependency on an initial package. These are the only packages
|
||||
// for which we load source code in typeCheck mode.
|
||||
// visit returns whether the package needs src or has a transitive
|
||||
// dependency on a package that does. These are the only packages
|
||||
// for which we load source code.
|
||||
var stack []*loaderPackage
|
||||
var visit func(lpkg *loaderPackage) bool
|
||||
visit = func(lpkg *loaderPackage) bool {
|
||||
|
@ -390,14 +356,15 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
|
|||
return lpkg.needsrc
|
||||
}
|
||||
|
||||
if ld.Mode >= LoadImports {
|
||||
// For each initial package, create its import DAG.
|
||||
for _, lpkg := range initial {
|
||||
visit(lpkg)
|
||||
}
|
||||
|
||||
// Load some/all packages from source, starting at
|
||||
}
|
||||
// Load type data if needed, starting at
|
||||
// the initial packages (roots of the import DAG).
|
||||
if ld.mode != metadata {
|
||||
if ld.Mode >= LoadTypes {
|
||||
var wg sync.WaitGroup
|
||||
for _, lpkg := range initial {
|
||||
wg.Add(1)
|
||||
|
@ -445,16 +412,16 @@ func (ld *loader) loadRecursive(lpkg *loaderPackage) {
|
|||
// after immediate dependencies are loaded.
|
||||
// Precondition: ld.mode != Metadata.
|
||||
func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||
if lpkg.PkgPath == "unsafe" {
|
||||
if lpkg.pkgpath == "unsafe" {
|
||||
// Fill in the blanks to avoid surprises.
|
||||
lpkg.Type = types.Unsafe
|
||||
lpkg.Types = types.Unsafe
|
||||
lpkg.Fset = ld.Fset
|
||||
lpkg.Files = []*ast.File{}
|
||||
lpkg.Info = new(types.Info)
|
||||
lpkg.Syntax = []*ast.File{}
|
||||
lpkg.TypesInfo = new(types.Info)
|
||||
return
|
||||
}
|
||||
|
||||
if ld.mode == typeCheck && !lpkg.needsrc {
|
||||
if !lpkg.needsrc {
|
||||
return // not a source package
|
||||
}
|
||||
|
||||
|
@ -469,20 +436,20 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
|||
lpkg.Errors = append(lpkg.Errors, err)
|
||||
}
|
||||
|
||||
files, errs := ld.parseFiles(lpkg.Srcs)
|
||||
files, errs := ld.parseFiles(lpkg.GoFiles)
|
||||
for _, err := range errs {
|
||||
appendError(err)
|
||||
}
|
||||
|
||||
lpkg.Fset = ld.Fset
|
||||
lpkg.Files = files
|
||||
lpkg.Syntax = files
|
||||
|
||||
// Call NewPackage directly with explicit name.
|
||||
// This avoids skew between golist and go/types when the files'
|
||||
// package declarations are inconsistent.
|
||||
lpkg.Type = types.NewPackage(lpkg.PkgPath, lpkg.Name)
|
||||
lpkg.Types = types.NewPackage(lpkg.pkgpath, lpkg.Name)
|
||||
|
||||
lpkg.Info = &types.Info{
|
||||
lpkg.TypesInfo = &types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
Uses: make(map[*ast.Ident]types.Object),
|
||||
|
@ -493,9 +460,6 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
|||
|
||||
// Copy the prototype types.Config as it must vary across Packages.
|
||||
tc := ld.TypeChecker // copy
|
||||
if !ld.cgo {
|
||||
tc.FakeImportC = true
|
||||
}
|
||||
tc.Importer = importerFunc(func(path string) (*types.Package, error) {
|
||||
if path == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
|
@ -513,11 +477,11 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
|||
// used to supply alternative file contents.
|
||||
return nil, fmt.Errorf("no metadata for %s", path)
|
||||
}
|
||||
if ipkg.Type != nil && ipkg.Type.Complete() {
|
||||
return ipkg.Type, nil
|
||||
if ipkg.Types != nil && ipkg.Types.Complete() {
|
||||
return ipkg.Types, nil
|
||||
}
|
||||
imp := ld.pkgs[ipkg.ID]
|
||||
if ld.mode == typeCheck && !imp.needsrc {
|
||||
if !imp.needsrc {
|
||||
return ld.loadFromExportData(imp)
|
||||
}
|
||||
log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg)
|
||||
|
@ -526,7 +490,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
|||
tc.Error = appendError
|
||||
|
||||
// type-check
|
||||
types.NewChecker(&tc, ld.Fset, lpkg.Type, lpkg.Info).Files(lpkg.Files)
|
||||
types.NewChecker(&tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
|
||||
|
||||
lpkg.importErrors = nil // no longer needed
|
||||
|
||||
|
@ -540,7 +504,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
|||
// we could just set IllTyped quietly.
|
||||
if tc.FakeImportC {
|
||||
outer:
|
||||
for _, f := range lpkg.Files {
|
||||
for _, f := range lpkg.Syntax {
|
||||
for _, imp := range f.Imports {
|
||||
if imp.Path.Value == `"C"` {
|
||||
appendError(fmt.Errorf(`%s: import "C" ignored`,
|
||||
|
@ -621,7 +585,7 @@ func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
|
|||
// loadFromExportData returns type information for the specified
|
||||
// package, loading it from an export data file on the first request.
|
||||
func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error) {
|
||||
if lpkg.PkgPath == "" {
|
||||
if lpkg.pkgpath == "" {
|
||||
log.Fatalf("internal error: Package %s has no PkgPath", lpkg)
|
||||
}
|
||||
|
||||
|
@ -640,7 +604,7 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
|
|||
ld.exportMu.Lock()
|
||||
defer ld.exportMu.Unlock()
|
||||
|
||||
if tpkg := lpkg.Type; tpkg != nil && tpkg.Complete() {
|
||||
if tpkg := lpkg.Types; tpkg != nil && tpkg.Complete() {
|
||||
return tpkg, nil // cache hit
|
||||
}
|
||||
|
||||
|
@ -688,7 +652,7 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
|
|||
// export data packages, processing of the A->B and A->C import
|
||||
// edges may occur in either order, depending on the sequence
|
||||
// of imports within A. If B is processed first, and its export
|
||||
// data mentions C, an imcomplete package for C will be created
|
||||
// data mentions C, an incomplete package for C will be created
|
||||
// before processing of C.)
|
||||
// We could do export data processing in topological order using
|
||||
// loadRecursive, but there's no parallelism to be gained.
|
||||
|
@ -704,8 +668,8 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
|
|||
visit = func(p *loaderPackage) {
|
||||
if !seen[p] {
|
||||
seen[p] = true
|
||||
if p.Type != nil {
|
||||
view[p.PkgPath] = p.Type
|
||||
if p.Types != nil {
|
||||
view[p.pkgpath] = p.Types
|
||||
} else {
|
||||
copyback = append(copyback, p)
|
||||
}
|
||||
|
@ -718,7 +682,7 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
|
|||
|
||||
// Parse the export data.
|
||||
// (May create/modify packages in view.)
|
||||
tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.PkgPath)
|
||||
tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.pkgpath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %s: %v", lpkg.export, err)
|
||||
}
|
||||
|
@ -726,34 +690,11 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
|
|||
// For each newly created types.Package in the view,
|
||||
// save it in the main graph.
|
||||
for _, p := range copyback {
|
||||
p.Type = view[p.PkgPath] // may still be nil
|
||||
p.Types = view[p.pkgpath] // may still be nil
|
||||
}
|
||||
|
||||
lpkg.Type = tpkg
|
||||
lpkg.Types = tpkg
|
||||
lpkg.IllTyped = false
|
||||
|
||||
return tpkg, nil
|
||||
}
|
||||
|
||||
// All returns a map, from package ID to package,
|
||||
// containing the packages in the given list and all their dependencies.
|
||||
// Each call to All returns a new map.
|
||||
//
|
||||
// TODO(rsc): I don't understand why this function exists.
|
||||
// It might be more useful to return a slice in dependency order.
|
||||
func All(list []*Package) map[string]*Package {
|
||||
all := make(map[string]*Package)
|
||||
var visit func(p *Package)
|
||||
visit = func(p *Package) {
|
||||
if all[p.ID] == nil {
|
||||
all[p.ID] = p
|
||||
for _, imp := range p.Imports {
|
||||
visit(imp)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, p := range list {
|
||||
visit(p)
|
||||
}
|
||||
return all
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
func TestGoIsTooOld(t *testing.T) {
|
||||
_, err := packages.Metadata(nil, "errors")
|
||||
_, err := packages.Load(nil, "errors")
|
||||
|
||||
if _, ok := err.(packages.GoTooOldError); !ok {
|
||||
t.Fatalf("using go/packages with pre-Go 1.11 go: err=%v, want ErrGoTooOld", err)
|
||||
|
|
|
@ -65,8 +65,11 @@ func TestMetadataImportGraph(t *testing.T) {
|
|||
})
|
||||
defer cleanup()
|
||||
|
||||
opts := &packages.Options{Env: append(os.Environ(), "GOPATH="+tmp)}
|
||||
initial, err := packages.Metadata(opts, "c", "subdir/d", "e")
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadImports,
|
||||
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||
}
|
||||
initial, err := packages.Load(cfg, "c", "subdir/d", "e")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -93,8 +96,8 @@ func TestMetadataImportGraph(t *testing.T) {
|
|||
t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
|
||||
}
|
||||
|
||||
opts.Tests = true
|
||||
initial, err = packages.Metadata(opts, "c", "subdir/d", "e")
|
||||
cfg.Tests = true
|
||||
initial, err = packages.Load(cfg, "c", "subdir/d", "e")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -174,7 +177,7 @@ func TestMetadataImportGraph(t *testing.T) {
|
|||
}
|
||||
|
||||
// Test an ad-hoc package, analogous to "go run hello.go".
|
||||
if initial, err := packages.Metadata(opts, filepath.Join(tmp, "src/c/c.go")); len(initial) == 0 {
|
||||
if initial, err := packages.Load(cfg, filepath.Join(tmp, "src/c/c.go")); len(initial) == 0 {
|
||||
t.Errorf("failed to obtain metadata for ad-hoc package: %s", err)
|
||||
} else {
|
||||
got := fmt.Sprintf("%s %s", initial[0].ID, srcs(initial[0]))
|
||||
|
@ -188,7 +191,7 @@ func TestMetadataImportGraph(t *testing.T) {
|
|||
// TODO(adonovan): test "all" returns everything in the current module.
|
||||
{
|
||||
// "..." (subdirectory)
|
||||
initial, err = packages.Metadata(opts, "subdir/...")
|
||||
initial, err = packages.Load(cfg, "subdir/...")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -225,12 +228,13 @@ func TestOptionsDir(t *testing.T) {
|
|||
{filepath.Join(tmp, "/src/a"), "./a", "packages not found"},
|
||||
{filepath.Join(tmp, "/src/a"), "./b", `"a/b"`},
|
||||
} {
|
||||
opts := &packages.Options{
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadSyntax, // Use LoadSyntax to ensure that files can be opened.
|
||||
Dir: test.dir,
|
||||
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||
}
|
||||
// Use TypeCheck to ensure that files can be opened.
|
||||
initial, err := packages.TypeCheck(opts, test.pattern)
|
||||
|
||||
initial, err := packages.Load(cfg, test.pattern)
|
||||
var got string
|
||||
if err != nil {
|
||||
got = err.Error()
|
||||
|
@ -266,8 +270,12 @@ func TestTypeCheckOK(t *testing.T) {
|
|||
})
|
||||
defer cleanup()
|
||||
|
||||
opts := &packages.Options{Env: append(os.Environ(), "GOPATH="+tmp), Error: func(error) {}}
|
||||
initial, err := packages.TypeCheck(opts, "a", "c")
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadSyntax,
|
||||
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||
Error: func(error) {},
|
||||
}
|
||||
initial, err := packages.Load(cfg, "a", "c")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -291,7 +299,7 @@ func TestTypeCheckOK(t *testing.T) {
|
|||
for _, test := range []struct {
|
||||
id string
|
||||
wantType bool
|
||||
wantFiles bool
|
||||
wantSyntax bool
|
||||
}{
|
||||
{"a", true, true}, // source package
|
||||
{"b", true, true}, // source package
|
||||
|
@ -304,15 +312,15 @@ func TestTypeCheckOK(t *testing.T) {
|
|||
t.Errorf("missing package: %s", test.id)
|
||||
continue
|
||||
}
|
||||
if (p.Type != nil) != test.wantType {
|
||||
if (p.Types != nil) != test.wantType {
|
||||
if test.wantType {
|
||||
t.Errorf("missing types.Package for %s", p)
|
||||
} else {
|
||||
t.Errorf("unexpected types.Package for %s", p)
|
||||
}
|
||||
}
|
||||
if (p.Files != nil) != test.wantFiles {
|
||||
if test.wantFiles {
|
||||
if (p.Syntax != nil) != test.wantSyntax {
|
||||
if test.wantSyntax {
|
||||
t.Errorf("missing ast.Files for %s", p)
|
||||
} else {
|
||||
t.Errorf("unexpected ast.Files for for %s", p)
|
||||
|
@ -347,18 +355,34 @@ func TestTypeCheckError(t *testing.T) {
|
|||
})
|
||||
defer cleanup()
|
||||
|
||||
opts := &packages.Options{Env: append(os.Environ(), "GOPATH="+tmp), Error: func(error) {}}
|
||||
initial, err := packages.TypeCheck(opts, "a", "c")
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadSyntax,
|
||||
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||
Error: func(error) {},
|
||||
}
|
||||
initial, err := packages.Load(cfg, "a", "c")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
all := packages.All(initial)
|
||||
all := make(map[string]*packages.Package)
|
||||
var visit func(p *packages.Package)
|
||||
visit = func(p *packages.Package) {
|
||||
if all[p.ID] == nil {
|
||||
all[p.ID] = p
|
||||
for _, imp := range p.Imports {
|
||||
visit(imp)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, p := range initial {
|
||||
visit(p)
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
id string
|
||||
wantType bool
|
||||
wantFiles bool
|
||||
wantTypes bool
|
||||
wantSyntax bool
|
||||
wantIllTyped bool
|
||||
wantErrs []string
|
||||
}{
|
||||
|
@ -373,15 +397,15 @@ func TestTypeCheckError(t *testing.T) {
|
|||
t.Errorf("missing package: %s", test.id)
|
||||
continue
|
||||
}
|
||||
if (p.Type != nil) != test.wantType {
|
||||
if test.wantType {
|
||||
if (p.Types != nil) != test.wantTypes {
|
||||
if test.wantTypes {
|
||||
t.Errorf("missing types.Package for %s", test.id)
|
||||
} else {
|
||||
t.Errorf("unexpected types.Package for %s", test.id)
|
||||
}
|
||||
}
|
||||
if (p.Files != nil) != test.wantFiles {
|
||||
if test.wantFiles {
|
||||
if (p.Syntax != nil) != test.wantSyntax {
|
||||
if test.wantSyntax {
|
||||
t.Errorf("missing ast.Files for %s", test.id)
|
||||
} else {
|
||||
t.Errorf("unexpected ast.Files for for %s", test.id)
|
||||
|
@ -439,12 +463,13 @@ func TestWholeProgramOverlay(t *testing.T) {
|
|||
}
|
||||
}
|
||||
var errs errCollector
|
||||
opts := &packages.Options{
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadAllSyntax,
|
||||
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||
Error: errs.add,
|
||||
ParseFile: parseFile,
|
||||
}
|
||||
initial, err := packages.WholeProgram(opts, "a")
|
||||
initial, err := packages.Load(cfg, "a")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
|
@ -483,11 +508,12 @@ import (
|
|||
os.Mkdir(filepath.Join(tmp, "src/empty"), 0777) // create an existing but empty package
|
||||
|
||||
var errs2 errCollector
|
||||
opts := &packages.Options{
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadAllSyntax,
|
||||
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||
Error: errs2.add,
|
||||
}
|
||||
initial, err := packages.WholeProgram(opts, "root")
|
||||
initial, err := packages.Load(cfg, "root")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -530,10 +556,10 @@ import (
|
|||
t.Errorf("missing package: %s", test.id)
|
||||
continue
|
||||
}
|
||||
if p.Type == nil {
|
||||
if p.Types == nil {
|
||||
t.Errorf("missing types.Package for %s", test.id)
|
||||
}
|
||||
if p.Files == nil {
|
||||
if p.Syntax == nil {
|
||||
t.Errorf("missing ast.Files for %s", test.id)
|
||||
}
|
||||
if !p.IllTyped {
|
||||
|
@ -560,7 +586,7 @@ func errorMessages(errors []error) []string {
|
|||
}
|
||||
|
||||
func srcs(p *packages.Package) (basenames []string) {
|
||||
for i, src := range p.Srcs {
|
||||
for i, src := range p.GoFiles {
|
||||
if strings.Contains(src, ".cache/go-build") {
|
||||
src = fmt.Sprintf("%d.go", i) // make cache names predictable
|
||||
} else {
|
||||
|
@ -671,5 +697,5 @@ func makeTree(t *testing.T, tree map[string]string) (dir string, cleanup func())
|
|||
}
|
||||
|
||||
func constant(p *packages.Package, name string) *types.Const {
|
||||
return p.Type.Scope().Lookup(name).(*types.Const)
|
||||
return p.Types.Scope().Lookup(name).(*types.Const)
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ func TestStdlibMetadata(t *testing.T) {
|
|||
alloc := memstats.Alloc
|
||||
|
||||
// Load, parse and type-check the program.
|
||||
pkgs, err := packages.Metadata(nil, "std")
|
||||
pkgs, err := packages.Load(nil, "std")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load metadata: %v", err)
|
||||
}
|
||||
|
@ -94,36 +94,28 @@ func TestCgoOption(t *testing.T) {
|
|||
{"net", "cgoLookupHost", "cgo_stub.go"},
|
||||
{"os/user", "current", "lookup_stubs.go"},
|
||||
} {
|
||||
for i := 0; i < 2; i++ { // !cgo, cgo
|
||||
opts := &packages.Options{
|
||||
DisableCgo: i == 0,
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadSyntax,
|
||||
Error: func(error) {},
|
||||
}
|
||||
pkgs, err := packages.TypeCheck(opts, test.pkg)
|
||||
pkgs, err := packages.Load(cfg, test.pkg)
|
||||
if err != nil {
|
||||
t.Errorf("Load failed: %v", err)
|
||||
continue
|
||||
}
|
||||
pkg := pkgs[0]
|
||||
obj := pkg.Type.Scope().Lookup(test.name)
|
||||
obj := pkg.Types.Scope().Lookup(test.name)
|
||||
if obj == nil {
|
||||
t.Errorf("no object %s.%s", test.pkg, test.name)
|
||||
continue
|
||||
}
|
||||
posn := pkg.Fset.Position(obj.Pos())
|
||||
if false {
|
||||
t.Logf("DisableCgo=%t, obj=%s, posn=%s", opts.DisableCgo, obj, posn)
|
||||
}
|
||||
|
||||
gotFile := filepath.Base(posn.Filename)
|
||||
filesMatch := gotFile == test.genericFile
|
||||
|
||||
if !opts.DisableCgo && filesMatch {
|
||||
if filesMatch {
|
||||
t.Errorf("!DisableCgo: %s found in %s, want native file",
|
||||
obj, gotFile)
|
||||
} else if opts.DisableCgo && !filesMatch {
|
||||
t.Errorf("DisableCgo: %s found in %s, want %s",
|
||||
obj, gotFile, test.genericFile)
|
||||
}
|
||||
|
||||
// Load the file and check the object is declared at the right place.
|
||||
|
@ -138,5 +130,4 @@ func TestCgoOption(t *testing.T) {
|
|||
t.Errorf("%s: %s not declared here (looking at %q)", posn, obj, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue