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
|
// golistPackages uses the "go list" command to expand the
|
||||||
// pattern words and return metadata for the specified packages.
|
// pattern words and return metadata for the specified packages.
|
||||||
// dir may be "" and env may be nil, as per os/exec.Command.
|
// 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;
|
// Fields must match go list;
|
||||||
// see $GOROOT/src/cmd/go/internal/load/pkg.go.
|
// see $GOROOT/src/cmd/go/internal/load/pkg.go.
|
||||||
type jsonPackage struct {
|
type jsonPackage struct {
|
||||||
|
@ -68,7 +68,7 @@ func golistPackages(ctx context.Context, dir string, env []string, cgo, export,
|
||||||
// Run "go list" for complete
|
// Run "go list" for complete
|
||||||
// information on the specified packages.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ func golistPackages(ctx context.Context, dir string, env []string, cgo, export,
|
||||||
}
|
}
|
||||||
for id := range ids {
|
for id := range ids {
|
||||||
// Go issue 26136: go list omits imports in cgo-generated files.
|
// Go issue 26136: go list omits imports in cgo-generated files.
|
||||||
if id == "C" && cgo {
|
if id == "C" {
|
||||||
imports["unsafe"] = "unsafe"
|
imports["unsafe"] = "unsafe"
|
||||||
imports["syscall"] = "syscall"
|
imports["syscall"] = "syscall"
|
||||||
if pkgpath != "runtime/cgo" {
|
if pkgpath != "runtime/cgo" {
|
||||||
|
@ -156,10 +156,10 @@ func golistPackages(ctx context.Context, dir string, env []string, cgo, export,
|
||||||
Package: &Package{
|
Package: &Package{
|
||||||
ID: id,
|
ID: id,
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
PkgPath: pkgpath,
|
GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
|
||||||
Srcs: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
|
OtherFiles: absJoin(p.Dir, p.SFiles, p.CFiles),
|
||||||
OtherSrcs: absJoin(p.Dir, p.SFiles, p.CFiles),
|
|
||||||
},
|
},
|
||||||
|
pkgpath: pkgpath,
|
||||||
imports: imports,
|
imports: imports,
|
||||||
export: export,
|
export: export,
|
||||||
indirect: p.DepOnly,
|
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.
|
// 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)
|
out := new(bytes.Buffer)
|
||||||
cmd := exec.CommandContext(ctx, "go", append([]string{
|
cmd := exec.CommandContext(ctx, "go", append([]string{
|
||||||
"list",
|
"list",
|
||||||
"-e",
|
"-e",
|
||||||
fmt.Sprintf("-cgo=%t", cgo),
|
fmt.Sprintf("-cgo=%t", true),
|
||||||
fmt.Sprintf("-test=%t", tests),
|
fmt.Sprintf("-test=%t", tests),
|
||||||
fmt.Sprintf("-export=%t", export),
|
fmt.Sprintf("-export=%t", export),
|
||||||
"-deps",
|
fmt.Sprintf("-deps=%t", deps),
|
||||||
"-json",
|
"-json",
|
||||||
"--",
|
"--",
|
||||||
}, args...)...)
|
}, args...)...)
|
||||||
|
@ -200,9 +200,6 @@ func golist(ctx context.Context, dir string, env []string, cgo, export, tests bo
|
||||||
if env == nil {
|
if env == nil {
|
||||||
env = os.Environ()
|
env = os.Environ()
|
||||||
}
|
}
|
||||||
if !cgo {
|
|
||||||
env = append(env, "CGO_ENABLED=0")
|
|
||||||
}
|
|
||||||
cmd.Env = env
|
cmd.Env = env
|
||||||
cmd.Dir = dir
|
cmd.Dir = dir
|
||||||
cmd.Stdout = out
|
cmd.Stdout = out
|
||||||
|
|
|
@ -28,8 +28,7 @@ import (
|
||||||
var (
|
var (
|
||||||
depsFlag = flag.Bool("deps", false, "show dependencies too")
|
depsFlag = flag.Bool("deps", false, "show dependencies too")
|
||||||
testFlag = flag.Bool("test", false, "include any tests implied by the patterns")
|
testFlag = flag.Bool("test", false, "include any tests implied by the patterns")
|
||||||
cgoFlag = flag.Bool("cgo", true, "process cgo files")
|
mode = flag.String("mode", "graph", "mode (one of metadata, graph, typed, alltyped)")
|
||||||
mode = flag.String("mode", "metadata", "mode (one of metadata, typecheck, wholeprogram)")
|
|
||||||
private = flag.Bool("private", false, "show non-exported declarations too")
|
private = flag.Bool("private", false, "show non-exported declarations too")
|
||||||
|
|
||||||
cpuprofile = flag.String("cpuprofile", "", "write CPU profile to this file")
|
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
|
// -mode flag
|
||||||
load := packages.TypeCheck
|
|
||||||
switch strings.ToLower(*mode) {
|
switch strings.ToLower(*mode) {
|
||||||
case "metadata":
|
case "files":
|
||||||
load = packages.Metadata
|
cfg.Mode = packages.LoadFiles
|
||||||
case "typecheck":
|
case "imports":
|
||||||
load = packages.TypeCheck
|
cfg.Mode = packages.LoadImports
|
||||||
case "wholeprogram":
|
case "types":
|
||||||
load = packages.WholeProgram
|
cfg.Mode = packages.LoadTypes
|
||||||
|
case "syntax":
|
||||||
|
cfg.Mode = packages.LoadSyntax
|
||||||
|
case "allsyntax":
|
||||||
|
cfg.Mode = packages.LoadAllSyntax
|
||||||
default:
|
default:
|
||||||
log.Fatalf("invalid mode: %s", *mode)
|
log.Fatalf("invalid mode: %s", *mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load, parse, and type-check the packages named on the command line.
|
lpkgs, err := packages.Load(cfg, flag.Args()...)
|
||||||
opts := &packages.Options{
|
|
||||||
Error: func(error) {}, // we'll take responsibility for printing errors
|
|
||||||
DisableCgo: !*cgoFlag,
|
|
||||||
Tests: *testFlag,
|
|
||||||
}
|
|
||||||
lpkgs, err := load(opts, flag.Args()...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
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("Go %s %q:\n", kind, lpkg.ID) // unique ID
|
||||||
fmt.Printf("\tpackage %s\n", lpkg.Name)
|
fmt.Printf("\tpackage %s\n", lpkg.Name)
|
||||||
fmt.Printf("\treflect.Type.PkgPath %q\n", lpkg.PkgPath)
|
|
||||||
|
|
||||||
// characterize type info
|
// characterize type info
|
||||||
if lpkg.Type == nil {
|
if lpkg.Types == nil {
|
||||||
fmt.Printf("\thas no exported type info\n")
|
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")
|
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")
|
fmt.Printf("\thas complete exported type info\n")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("\thas complete exported type info and typed ASTs\n")
|
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")
|
fmt.Printf("\thas an error among its dependencies\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// source files
|
// source files
|
||||||
for _, src := range lpkg.Srcs {
|
for _, src := range lpkg.GoFiles {
|
||||||
fmt.Printf("\tfile %s\n", src)
|
fmt.Printf("\tfile %s\n", src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,9 +219,9 @@ func print(lpkg *packages.Package) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// package members (TypeCheck or WholeProgram mode)
|
// package members (TypeCheck or WholeProgram mode)
|
||||||
if lpkg.Type != nil {
|
if lpkg.Types != nil {
|
||||||
qual := types.RelativeTo(lpkg.Type)
|
qual := types.RelativeTo(lpkg.Types)
|
||||||
scope := lpkg.Type.Scope()
|
scope := lpkg.Types.Scope()
|
||||||
for _, name := range scope.Names() {
|
for _, name := range scope.Names() {
|
||||||
obj := scope.Lookup(name)
|
obj := scope.Lookup(name)
|
||||||
if !obj.Exported() && !*private {
|
if !obj.Exported() && !*private {
|
||||||
|
|
|
@ -20,10 +20,41 @@ import (
|
||||||
"golang.org/x/tools/go/gcexportdata"
|
"golang.org/x/tools/go/gcexportdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An Options specifies details about how packages should be loaded.
|
// A LoadMode specifies the amount of detail to return when loading packages.
|
||||||
// The loaders do not modify this struct.
|
type LoadMode int
|
||||||
// TODO(rsc): Better name would be Config.
|
|
||||||
type Options struct {
|
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.
|
// Context specifies the context for the load operation.
|
||||||
// If the context is cancelled, the loader may stop early
|
// If the context is cancelled, the loader may stop early
|
||||||
// and return an ErrCancelled error.
|
// and return an ErrCancelled error.
|
||||||
|
@ -35,14 +66,6 @@ type Options struct {
|
||||||
// If Dir is empty, the tool is run in the current directory.
|
// If Dir is empty, the tool is run in the current directory.
|
||||||
Dir string
|
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.
|
// Env is the environment to use when invoking the build system tool.
|
||||||
// If Env is nil, the current environment is used.
|
// If Env is nil, the current environment is used.
|
||||||
// Like in os/exec's Cmd, only the last value in the slice for
|
// Like in os/exec's Cmd, only the last value in the slice for
|
||||||
|
@ -110,37 +133,11 @@ type Options struct {
|
||||||
TypeChecker types.Config
|
TypeChecker types.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata loads and returns the Go packages named by the given patterns,
|
// Load and returns the Go packages named by the given patterns.
|
||||||
// omitting type information and type-checked syntax trees from all packages.
|
func Load(cfg *Config, patterns ...string) ([]*Package, error) {
|
||||||
// TODO(rsc): Better name would be Load.
|
l := &loader{}
|
||||||
func Metadata(o *Options, patterns ...string) ([]*Package, error) {
|
if cfg != nil {
|
||||||
l := &loader{mode: metadata}
|
l.Config = *cfg
|
||||||
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
|
|
||||||
}
|
}
|
||||||
return l.load(patterns...)
|
return l.load(patterns...)
|
||||||
}
|
}
|
||||||
|
@ -155,18 +152,6 @@ type Package struct {
|
||||||
// interpret them.
|
// interpret them.
|
||||||
ID string
|
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 is the package name as it appears in the package source code.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
|
@ -179,7 +164,7 @@ type Package struct {
|
||||||
// to corresponding loaded Packages.
|
// to corresponding loaded Packages.
|
||||||
Imports map[string]*Package
|
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,
|
// If a package has typed syntax trees and the DisableCgo option is false,
|
||||||
// the cgo-processed output files are listed instead of the original
|
// 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
|
// TODO(rsc): Actually, in TypeCheck mode even the packages without
|
||||||
// syntax trees (pure dependencies) lose their original sources.
|
// syntax trees (pure dependencies) lose their original sources.
|
||||||
// We should fix that.
|
// We should fix that.
|
||||||
//
|
GoFiles []string
|
||||||
// TODO(rsc): This should be GoFiles.
|
|
||||||
Srcs []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.
|
// including assembly, C, C++, Fortran, Objective-C, SWIG, and so on.
|
||||||
//
|
OtherFiles []string
|
||||||
// TODO(rsc): This should be OtherFiles.
|
|
||||||
OtherSrcs []string
|
|
||||||
|
|
||||||
// Type is the type information for the package.
|
// Type is the type information for the package.
|
||||||
// The TypeCheck and WholeProgram loaders set this field for all packages.
|
// The TypeCheck and WholeProgram loaders set this field for all packages.
|
||||||
// TODO(rsc): This should be Types.
|
Types *types.Package
|
||||||
Type *types.Package
|
|
||||||
|
|
||||||
// IllTyped indicates whether the package has any type errors.
|
// IllTyped indicates whether the package has any type errors.
|
||||||
// The TypeCheck and WholeProgram loaders set this field for all packages.
|
// 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 TypeCheck loader sets Files for packages matching the patterns.
|
||||||
// The WholeProgram loader sets Files for all packages, including dependencies.
|
// The WholeProgram loader sets Files for all packages, including dependencies.
|
||||||
//
|
Syntax []*ast.File
|
||||||
// TODO(rsc): This should be ASTs or Syntax.
|
|
||||||
Files []*ast.File
|
|
||||||
|
|
||||||
// Info is the type-checking results for the package's syntax trees.
|
// Info is the type-checking results for the package's syntax trees.
|
||||||
// It is set only when Files is set.
|
// It is set only when Files is set.
|
||||||
//
|
TypesInfo *types.Info
|
||||||
// TODO(rsc): This should be TypesInfo.
|
|
||||||
Info *types.Info
|
|
||||||
|
|
||||||
// Fset is the token.FileSet for the package's syntax trees listed in Files.
|
// Fset is the token.FileSet for the package's syntax trees listed in Files.
|
||||||
// It is set only when Files is set.
|
// It is set only when Files is set.
|
||||||
|
@ -249,42 +225,27 @@ type loaderPackage struct {
|
||||||
loadOnce sync.Once
|
loadOnce sync.Once
|
||||||
color uint8 // for cycle detection
|
color uint8 // for cycle detection
|
||||||
mark, needsrc bool // used in TypeCheck mode only
|
mark, needsrc bool // used in TypeCheck mode only
|
||||||
|
pkgpath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lpkg *Package) String() string { return lpkg.ID }
|
func (lpkg *Package) String() string { return lpkg.ID }
|
||||||
|
|
||||||
// loader holds the working state of a single call to load.
|
// loader holds the working state of a single call to load.
|
||||||
type loader struct {
|
type loader struct {
|
||||||
mode mode
|
|
||||||
cgo bool
|
|
||||||
pkgs map[string]*loaderPackage
|
pkgs map[string]*loaderPackage
|
||||||
Options
|
Config
|
||||||
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
|
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) {
|
func (ld *loader) load(patterns ...string) ([]*Package, error) {
|
||||||
if ld.Context == nil {
|
if ld.Context == nil {
|
||||||
ld.Context = context.Background()
|
ld.Context = context.Background()
|
||||||
}
|
}
|
||||||
|
if ld.Mode >= LoadSyntax {
|
||||||
if ld.mode > metadata {
|
|
||||||
if ld.Fset == nil {
|
if ld.Fset == nil {
|
||||||
ld.Fset = token.NewFileSet()
|
ld.Fset = token.NewFileSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
ld.cgo = !ld.DisableCgo
|
|
||||||
|
|
||||||
if ld.Error == nil {
|
if ld.Error == nil {
|
||||||
ld.Error = func(e error) {
|
ld.Error = func(e error) {
|
||||||
fmt.Fprintln(os.Stderr, e)
|
fmt.Fprintln(os.Stderr, e)
|
||||||
|
@ -305,7 +266,9 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
|
||||||
|
|
||||||
// Do the metadata query and partial build.
|
// Do the metadata query and partial build.
|
||||||
// TODO(adonovan): support alternative build systems at this seam.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -319,10 +282,13 @@ func (ld *loader) load(patterns ...string) ([]*Package, error) {
|
||||||
if !pkg.indirect {
|
if !pkg.indirect {
|
||||||
initial = append(initial, pkg)
|
initial = append(initial, pkg)
|
||||||
|
|
||||||
if ld.mode == typeCheck {
|
if ld.Mode == LoadSyntax {
|
||||||
pkg.needsrc = true
|
pkg.needsrc = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ld.Mode >= LoadAllSyntax {
|
||||||
|
pkg.needsrc = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if len(ld.pkgs) == 0 {
|
if len(ld.pkgs) == 0 {
|
||||||
return nil, fmt.Errorf("packages not found")
|
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.
|
// 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.
|
// 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
|
// visit returns whether the package needs src or has a transitive
|
||||||
// dependency on an initial package. These are the only packages
|
// dependency on a package that does. These are the only packages
|
||||||
// for which we load source code in typeCheck mode.
|
// for which we load source code.
|
||||||
var stack []*loaderPackage
|
var stack []*loaderPackage
|
||||||
var visit func(lpkg *loaderPackage) bool
|
var visit func(lpkg *loaderPackage) bool
|
||||||
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
|
return lpkg.needsrc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ld.Mode >= LoadImports {
|
||||||
// For each initial package, create its import DAG.
|
// For each initial package, create its import DAG.
|
||||||
for _, lpkg := range initial {
|
for _, lpkg := range initial {
|
||||||
visit(lpkg)
|
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).
|
// the initial packages (roots of the import DAG).
|
||||||
if ld.mode != metadata {
|
if ld.Mode >= LoadTypes {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for _, lpkg := range initial {
|
for _, lpkg := range initial {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
@ -445,16 +412,16 @@ func (ld *loader) loadRecursive(lpkg *loaderPackage) {
|
||||||
// after immediate dependencies are loaded.
|
// after immediate dependencies are loaded.
|
||||||
// Precondition: ld.mode != Metadata.
|
// Precondition: ld.mode != Metadata.
|
||||||
func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||||
if lpkg.PkgPath == "unsafe" {
|
if lpkg.pkgpath == "unsafe" {
|
||||||
// Fill in the blanks to avoid surprises.
|
// Fill in the blanks to avoid surprises.
|
||||||
lpkg.Type = types.Unsafe
|
lpkg.Types = types.Unsafe
|
||||||
lpkg.Fset = ld.Fset
|
lpkg.Fset = ld.Fset
|
||||||
lpkg.Files = []*ast.File{}
|
lpkg.Syntax = []*ast.File{}
|
||||||
lpkg.Info = new(types.Info)
|
lpkg.TypesInfo = new(types.Info)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ld.mode == typeCheck && !lpkg.needsrc {
|
if !lpkg.needsrc {
|
||||||
return // not a source package
|
return // not a source package
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,20 +436,20 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||||
lpkg.Errors = append(lpkg.Errors, err)
|
lpkg.Errors = append(lpkg.Errors, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
files, errs := ld.parseFiles(lpkg.Srcs)
|
files, errs := ld.parseFiles(lpkg.GoFiles)
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
appendError(err)
|
appendError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
lpkg.Fset = ld.Fset
|
lpkg.Fset = ld.Fset
|
||||||
lpkg.Files = files
|
lpkg.Syntax = files
|
||||||
|
|
||||||
// Call NewPackage directly with explicit name.
|
// Call NewPackage directly with explicit name.
|
||||||
// This avoids skew between golist and go/types when the files'
|
// This avoids skew between golist and go/types when the files'
|
||||||
// package declarations are inconsistent.
|
// 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),
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||||
Defs: make(map[*ast.Ident]types.Object),
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
Uses: 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.
|
// Copy the prototype types.Config as it must vary across Packages.
|
||||||
tc := ld.TypeChecker // copy
|
tc := ld.TypeChecker // copy
|
||||||
if !ld.cgo {
|
|
||||||
tc.FakeImportC = true
|
|
||||||
}
|
|
||||||
tc.Importer = importerFunc(func(path string) (*types.Package, error) {
|
tc.Importer = importerFunc(func(path string) (*types.Package, error) {
|
||||||
if path == "unsafe" {
|
if path == "unsafe" {
|
||||||
return types.Unsafe, nil
|
return types.Unsafe, nil
|
||||||
|
@ -513,11 +477,11 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||||
// used to supply alternative file contents.
|
// used to supply alternative file contents.
|
||||||
return nil, fmt.Errorf("no metadata for %s", path)
|
return nil, fmt.Errorf("no metadata for %s", path)
|
||||||
}
|
}
|
||||||
if ipkg.Type != nil && ipkg.Type.Complete() {
|
if ipkg.Types != nil && ipkg.Types.Complete() {
|
||||||
return ipkg.Type, nil
|
return ipkg.Types, nil
|
||||||
}
|
}
|
||||||
imp := ld.pkgs[ipkg.ID]
|
imp := ld.pkgs[ipkg.ID]
|
||||||
if ld.mode == typeCheck && !imp.needsrc {
|
if !imp.needsrc {
|
||||||
return ld.loadFromExportData(imp)
|
return ld.loadFromExportData(imp)
|
||||||
}
|
}
|
||||||
log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg)
|
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
|
tc.Error = appendError
|
||||||
|
|
||||||
// type-check
|
// 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
|
lpkg.importErrors = nil // no longer needed
|
||||||
|
|
||||||
|
@ -540,7 +504,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||||
// we could just set IllTyped quietly.
|
// we could just set IllTyped quietly.
|
||||||
if tc.FakeImportC {
|
if tc.FakeImportC {
|
||||||
outer:
|
outer:
|
||||||
for _, f := range lpkg.Files {
|
for _, f := range lpkg.Syntax {
|
||||||
for _, imp := range f.Imports {
|
for _, imp := range f.Imports {
|
||||||
if imp.Path.Value == `"C"` {
|
if imp.Path.Value == `"C"` {
|
||||||
appendError(fmt.Errorf(`%s: import "C" ignored`,
|
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
|
// loadFromExportData returns type information for the specified
|
||||||
// package, loading it from an export data file on the first request.
|
// package, loading it from an export data file on the first request.
|
||||||
func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error) {
|
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)
|
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()
|
ld.exportMu.Lock()
|
||||||
defer ld.exportMu.Unlock()
|
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
|
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
|
// export data packages, processing of the A->B and A->C import
|
||||||
// edges may occur in either order, depending on the sequence
|
// edges may occur in either order, depending on the sequence
|
||||||
// of imports within A. If B is processed first, and its export
|
// 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.)
|
// before processing of C.)
|
||||||
// We could do export data processing in topological order using
|
// We could do export data processing in topological order using
|
||||||
// loadRecursive, but there's no parallelism to be gained.
|
// 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) {
|
visit = func(p *loaderPackage) {
|
||||||
if !seen[p] {
|
if !seen[p] {
|
||||||
seen[p] = true
|
seen[p] = true
|
||||||
if p.Type != nil {
|
if p.Types != nil {
|
||||||
view[p.PkgPath] = p.Type
|
view[p.pkgpath] = p.Types
|
||||||
} else {
|
} else {
|
||||||
copyback = append(copyback, p)
|
copyback = append(copyback, p)
|
||||||
}
|
}
|
||||||
|
@ -718,7 +682,7 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
|
||||||
|
|
||||||
// Parse the export data.
|
// Parse the export data.
|
||||||
// (May create/modify packages in view.)
|
// (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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("reading %s: %v", lpkg.export, err)
|
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,
|
// For each newly created types.Package in the view,
|
||||||
// save it in the main graph.
|
// save it in the main graph.
|
||||||
for _, p := range copyback {
|
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
|
lpkg.IllTyped = false
|
||||||
|
|
||||||
return tpkg, nil
|
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) {
|
func TestGoIsTooOld(t *testing.T) {
|
||||||
_, err := packages.Metadata(nil, "errors")
|
_, err := packages.Load(nil, "errors")
|
||||||
|
|
||||||
if _, ok := err.(packages.GoTooOldError); !ok {
|
if _, ok := err.(packages.GoTooOldError); !ok {
|
||||||
t.Fatalf("using go/packages with pre-Go 1.11 go: err=%v, want ErrGoTooOld", err)
|
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()
|
defer cleanup()
|
||||||
|
|
||||||
opts := &packages.Options{Env: append(os.Environ(), "GOPATH="+tmp)}
|
cfg := &packages.Config{
|
||||||
initial, err := packages.Metadata(opts, "c", "subdir/d", "e")
|
Mode: packages.LoadImports,
|
||||||
|
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||||
|
}
|
||||||
|
initial, err := packages.Load(cfg, "c", "subdir/d", "e")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -93,8 +96,8 @@ func TestMetadataImportGraph(t *testing.T) {
|
||||||
t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
|
t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Tests = true
|
cfg.Tests = true
|
||||||
initial, err = packages.Metadata(opts, "c", "subdir/d", "e")
|
initial, err = packages.Load(cfg, "c", "subdir/d", "e")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -174,7 +177,7 @@ func TestMetadataImportGraph(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test an ad-hoc package, analogous to "go run hello.go".
|
// 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)
|
t.Errorf("failed to obtain metadata for ad-hoc package: %s", err)
|
||||||
} else {
|
} else {
|
||||||
got := fmt.Sprintf("%s %s", initial[0].ID, srcs(initial[0]))
|
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.
|
// TODO(adonovan): test "all" returns everything in the current module.
|
||||||
{
|
{
|
||||||
// "..." (subdirectory)
|
// "..." (subdirectory)
|
||||||
initial, err = packages.Metadata(opts, "subdir/...")
|
initial, err = packages.Load(cfg, "subdir/...")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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"), "./a", "packages not found"},
|
||||||
{filepath.Join(tmp, "/src/a"), "./b", `"a/b"`},
|
{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,
|
Dir: test.dir,
|
||||||
Env: append(os.Environ(), "GOPATH="+tmp),
|
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
|
var got string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
got = err.Error()
|
got = err.Error()
|
||||||
|
@ -266,8 +270,12 @@ func TestTypeCheckOK(t *testing.T) {
|
||||||
})
|
})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
opts := &packages.Options{Env: append(os.Environ(), "GOPATH="+tmp), Error: func(error) {}}
|
cfg := &packages.Config{
|
||||||
initial, err := packages.TypeCheck(opts, "a", "c")
|
Mode: packages.LoadSyntax,
|
||||||
|
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||||
|
Error: func(error) {},
|
||||||
|
}
|
||||||
|
initial, err := packages.Load(cfg, "a", "c")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -291,7 +299,7 @@ func TestTypeCheckOK(t *testing.T) {
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
id string
|
id string
|
||||||
wantType bool
|
wantType bool
|
||||||
wantFiles bool
|
wantSyntax bool
|
||||||
}{
|
}{
|
||||||
{"a", true, true}, // source package
|
{"a", true, true}, // source package
|
||||||
{"b", 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)
|
t.Errorf("missing package: %s", test.id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (p.Type != nil) != test.wantType {
|
if (p.Types != nil) != test.wantType {
|
||||||
if test.wantType {
|
if test.wantType {
|
||||||
t.Errorf("missing types.Package for %s", p)
|
t.Errorf("missing types.Package for %s", p)
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("unexpected types.Package for %s", p)
|
t.Errorf("unexpected types.Package for %s", p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (p.Files != nil) != test.wantFiles {
|
if (p.Syntax != nil) != test.wantSyntax {
|
||||||
if test.wantFiles {
|
if test.wantSyntax {
|
||||||
t.Errorf("missing ast.Files for %s", p)
|
t.Errorf("missing ast.Files for %s", p)
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("unexpected ast.Files for for %s", p)
|
t.Errorf("unexpected ast.Files for for %s", p)
|
||||||
|
@ -347,18 +355,34 @@ func TestTypeCheckError(t *testing.T) {
|
||||||
})
|
})
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
opts := &packages.Options{Env: append(os.Environ(), "GOPATH="+tmp), Error: func(error) {}}
|
cfg := &packages.Config{
|
||||||
initial, err := packages.TypeCheck(opts, "a", "c")
|
Mode: packages.LoadSyntax,
|
||||||
|
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||||
|
Error: func(error) {},
|
||||||
|
}
|
||||||
|
initial, err := packages.Load(cfg, "a", "c")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
for _, test := range []struct {
|
||||||
id string
|
id string
|
||||||
wantType bool
|
wantTypes bool
|
||||||
wantFiles bool
|
wantSyntax bool
|
||||||
wantIllTyped bool
|
wantIllTyped bool
|
||||||
wantErrs []string
|
wantErrs []string
|
||||||
}{
|
}{
|
||||||
|
@ -373,15 +397,15 @@ func TestTypeCheckError(t *testing.T) {
|
||||||
t.Errorf("missing package: %s", test.id)
|
t.Errorf("missing package: %s", test.id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (p.Type != nil) != test.wantType {
|
if (p.Types != nil) != test.wantTypes {
|
||||||
if test.wantType {
|
if test.wantTypes {
|
||||||
t.Errorf("missing types.Package for %s", test.id)
|
t.Errorf("missing types.Package for %s", test.id)
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("unexpected types.Package for %s", test.id)
|
t.Errorf("unexpected types.Package for %s", test.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (p.Files != nil) != test.wantFiles {
|
if (p.Syntax != nil) != test.wantSyntax {
|
||||||
if test.wantFiles {
|
if test.wantSyntax {
|
||||||
t.Errorf("missing ast.Files for %s", test.id)
|
t.Errorf("missing ast.Files for %s", test.id)
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("unexpected ast.Files for for %s", test.id)
|
t.Errorf("unexpected ast.Files for for %s", test.id)
|
||||||
|
@ -439,12 +463,13 @@ func TestWholeProgramOverlay(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var errs errCollector
|
var errs errCollector
|
||||||
opts := &packages.Options{
|
cfg := &packages.Config{
|
||||||
|
Mode: packages.LoadAllSyntax,
|
||||||
Env: append(os.Environ(), "GOPATH="+tmp),
|
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||||
Error: errs.add,
|
Error: errs.add,
|
||||||
ParseFile: parseFile,
|
ParseFile: parseFile,
|
||||||
}
|
}
|
||||||
initial, err := packages.WholeProgram(opts, "a")
|
initial, err := packages.Load(cfg, "a")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
continue
|
continue
|
||||||
|
@ -483,11 +508,12 @@ import (
|
||||||
os.Mkdir(filepath.Join(tmp, "src/empty"), 0777) // create an existing but empty package
|
os.Mkdir(filepath.Join(tmp, "src/empty"), 0777) // create an existing but empty package
|
||||||
|
|
||||||
var errs2 errCollector
|
var errs2 errCollector
|
||||||
opts := &packages.Options{
|
cfg := &packages.Config{
|
||||||
|
Mode: packages.LoadAllSyntax,
|
||||||
Env: append(os.Environ(), "GOPATH="+tmp),
|
Env: append(os.Environ(), "GOPATH="+tmp),
|
||||||
Error: errs2.add,
|
Error: errs2.add,
|
||||||
}
|
}
|
||||||
initial, err := packages.WholeProgram(opts, "root")
|
initial, err := packages.Load(cfg, "root")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -530,10 +556,10 @@ import (
|
||||||
t.Errorf("missing package: %s", test.id)
|
t.Errorf("missing package: %s", test.id)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if p.Type == nil {
|
if p.Types == nil {
|
||||||
t.Errorf("missing types.Package for %s", test.id)
|
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)
|
t.Errorf("missing ast.Files for %s", test.id)
|
||||||
}
|
}
|
||||||
if !p.IllTyped {
|
if !p.IllTyped {
|
||||||
|
@ -560,7 +586,7 @@ func errorMessages(errors []error) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func srcs(p *packages.Package) (basenames []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") {
|
if strings.Contains(src, ".cache/go-build") {
|
||||||
src = fmt.Sprintf("%d.go", i) // make cache names predictable
|
src = fmt.Sprintf("%d.go", i) // make cache names predictable
|
||||||
} else {
|
} 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 {
|
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
|
alloc := memstats.Alloc
|
||||||
|
|
||||||
// Load, parse and type-check the program.
|
// Load, parse and type-check the program.
|
||||||
pkgs, err := packages.Metadata(nil, "std")
|
pkgs, err := packages.Load(nil, "std")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to load metadata: %v", err)
|
t.Fatalf("failed to load metadata: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -94,36 +94,28 @@ func TestCgoOption(t *testing.T) {
|
||||||
{"net", "cgoLookupHost", "cgo_stub.go"},
|
{"net", "cgoLookupHost", "cgo_stub.go"},
|
||||||
{"os/user", "current", "lookup_stubs.go"},
|
{"os/user", "current", "lookup_stubs.go"},
|
||||||
} {
|
} {
|
||||||
for i := 0; i < 2; i++ { // !cgo, cgo
|
cfg := &packages.Config{
|
||||||
opts := &packages.Options{
|
Mode: packages.LoadSyntax,
|
||||||
DisableCgo: i == 0,
|
|
||||||
Error: func(error) {},
|
Error: func(error) {},
|
||||||
}
|
}
|
||||||
pkgs, err := packages.TypeCheck(opts, test.pkg)
|
pkgs, err := packages.Load(cfg, test.pkg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Load failed: %v", err)
|
t.Errorf("Load failed: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pkg := pkgs[0]
|
pkg := pkgs[0]
|
||||||
obj := pkg.Type.Scope().Lookup(test.name)
|
obj := pkg.Types.Scope().Lookup(test.name)
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
t.Errorf("no object %s.%s", test.pkg, test.name)
|
t.Errorf("no object %s.%s", test.pkg, test.name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
posn := pkg.Fset.Position(obj.Pos())
|
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)
|
gotFile := filepath.Base(posn.Filename)
|
||||||
filesMatch := gotFile == test.genericFile
|
filesMatch := gotFile == test.genericFile
|
||||||
|
|
||||||
if !opts.DisableCgo && filesMatch {
|
if filesMatch {
|
||||||
t.Errorf("!DisableCgo: %s found in %s, want native file",
|
t.Errorf("!DisableCgo: %s found in %s, want native file",
|
||||||
obj, gotFile)
|
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.
|
// Load the file and check the object is declared at the right place.
|
||||||
|
@ -139,4 +131,3 @@ func TestCgoOption(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue