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:
Ian Cottrell 2018-07-23 16:29:28 -04:00
parent ded554d068
commit 214274eeeb
6 changed files with 223 additions and 266 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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
}

View File

@ -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)

View File

@ -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)
}

View File

@ -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)
}
}
}
}