go.tools/go/loader: permit Create* methods to specify the ad-hoc package's path
CL 49530047 made the (over-)simplifying assumption that the Path of an ad-hoc (Created) package should default to its Name, i.e. package declaration. With this change, the Name is still always computed from the package declaration (by go/types) but the Path may be specified by the loader.Config. If "", the value of the Name is used, which is not globally unique. R=gri, axwalk CC=golang-codereviews https://golang.org/cl/55180043
This commit is contained in:
parent
074bd4ac9c
commit
0dcaae1610
|
@ -25,16 +25,17 @@
|
|||
// // See FromArgsUsage for help.
|
||||
// rest, err := conf.FromArgs(os.Args[1:])
|
||||
//
|
||||
// // Parse the specified files and create an ad-hoc package.
|
||||
// // Parse the specified files and create an ad-hoc package with path "foo".
|
||||
// // All files must have the same 'package' declaration.
|
||||
// err := conf.CreateFromFilenames("foo.go", "bar.go")
|
||||
// err := conf.CreateFromFilenames("foo", "foo.go", "bar.go")
|
||||
//
|
||||
// // Create an ad-hoc package from the specified already-parsed files.
|
||||
// // Create an ad-hoc package with path "foo" from
|
||||
// // the specified already-parsed files.
|
||||
// // All ASTs must have the same 'package' declaration.
|
||||
// err := conf.CreateFromFiles(parsedFiles)
|
||||
// err := conf.CreateFromFiles("foo", parsedFiles)
|
||||
//
|
||||
// // Add "runtime" to the set of packages to be loaded.
|
||||
// err := conf.Import("runtime")
|
||||
// conf.Import("runtime")
|
||||
//
|
||||
// // Adds "fmt" and "fmt_test" to the set of packages
|
||||
// // to be loaded. "fmt" will include *_test.go files.
|
||||
|
@ -92,6 +93,8 @@ package loader
|
|||
// (*Config).CreateFromFiles has a nasty precondition.
|
||||
// - Ideally some of this logic would move under the umbrella of
|
||||
// go/types; see bug 7114.
|
||||
// - s/path/importPath/g to avoid ambiguity with other meanings of
|
||||
// "path": a file name, a colon-separated directory list.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -157,13 +160,15 @@ type Config struct {
|
|||
Build *build.Context
|
||||
|
||||
// CreatePkgs specifies a list of non-importable initial
|
||||
// packages to create. Each element is a list of parsed files
|
||||
// to be type-checked into a new package whose name is taken
|
||||
// from ast.File.Package.
|
||||
// packages to create. Each element specifies a list of
|
||||
// parsed files to be type-checked into a new package, and a
|
||||
// path for that package. If the path is "", the package's
|
||||
// name will be used instead. The path needn't be globally
|
||||
// unique.
|
||||
//
|
||||
// The resulting packages will appear in the corresponding
|
||||
// elements of the Program.Created slice.
|
||||
CreatePkgs [][]*ast.File
|
||||
CreatePkgs []CreatePkg
|
||||
|
||||
// ImportPkgs specifies a set of initial packages to load from
|
||||
// source. The map keys are package import paths, used to
|
||||
|
@ -176,6 +181,11 @@ type Config struct {
|
|||
ImportPkgs map[string]bool
|
||||
}
|
||||
|
||||
type CreatePkg struct {
|
||||
Path string
|
||||
Files []*ast.File
|
||||
}
|
||||
|
||||
// A Program is a Go program loaded from source or binary
|
||||
// as specified by a Config.
|
||||
type Program struct {
|
||||
|
@ -224,9 +234,8 @@ Each argument may take one of two forms:
|
|||
1. A comma-separated list of *.go source files.
|
||||
|
||||
All of the specified files are loaded, parsed and type-checked
|
||||
as a single package. The name of the package is taken from the
|
||||
files' package declarations, which must all be equal. All the
|
||||
files must belong to the same directory.
|
||||
as a single package.
|
||||
All the files must belong to the same directory.
|
||||
|
||||
2. An import path.
|
||||
|
||||
|
@ -268,8 +277,8 @@ func (conf *Config) FromArgs(args []string) (rest []string, err error) {
|
|||
|
||||
if strings.HasSuffix(arg, ".go") {
|
||||
// Assume arg is a comma-separated list of *.go files
|
||||
// comprising a single package.
|
||||
err = conf.CreateFromFilenames(strings.Split(arg, ",")...)
|
||||
// denoting a single ad-hoc package.
|
||||
err = conf.CreateFromFilenames("", strings.Split(arg, ",")...)
|
||||
} else {
|
||||
// Assume arg is a directory name denoting a
|
||||
// package, perhaps plus an external test
|
||||
|
@ -291,27 +300,27 @@ func (conf *Config) FromArgs(args []string) (rest []string, err error) {
|
|||
// specified *.go files and adds a package entry for them to
|
||||
// conf.CreatePkgs.
|
||||
//
|
||||
func (conf *Config) CreateFromFilenames(filenames ...string) error {
|
||||
func (conf *Config) CreateFromFilenames(path string, filenames ...string) error {
|
||||
files, err := parseFiles(conf.fset(), ".", filenames...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conf.CreateFromFiles(files...)
|
||||
conf.CreateFromFiles(path, files...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateFromFiles is a convenience function that adds a CreatePkgs
|
||||
// entry for the specified parsed files.
|
||||
// entry to create package of the specified path and parsed files.
|
||||
//
|
||||
// Precondition: conf.Fset is non-nil and was the fileset used to parse
|
||||
// the files. (e.g. the files came from conf.ParseFile().)
|
||||
//
|
||||
func (conf *Config) CreateFromFiles(files ...*ast.File) {
|
||||
func (conf *Config) CreateFromFiles(path string, files ...*ast.File) {
|
||||
if conf.Fset == nil {
|
||||
panic("nil Fset")
|
||||
}
|
||||
conf.CreatePkgs = append(conf.CreatePkgs, files)
|
||||
conf.CreatePkgs = append(conf.CreatePkgs, CreatePkg{path, files})
|
||||
}
|
||||
|
||||
// ImportWithTests is a convenience function that adds path to
|
||||
|
@ -320,7 +329,7 @@ func (conf *Config) CreateFromFiles(files ...*ast.File) {
|
|||
// its directory that contain a "package x" (not "package x_test")
|
||||
// declaration.
|
||||
//
|
||||
// In addition, if any *_test.go files contain a "package <path>_test"
|
||||
// In addition, if any *_test.go files contain a "package x_test"
|
||||
// declaration, an additional package comprising just those files will
|
||||
// be added to CreatePkgs.
|
||||
//
|
||||
|
@ -344,7 +353,7 @@ func (conf *Config) ImportWithTests(path string) error {
|
|||
return err
|
||||
}
|
||||
if len(xtestFiles) > 0 {
|
||||
conf.CreateFromFiles(xtestFiles...)
|
||||
conf.CreateFromFiles(path+"_test", xtestFiles...)
|
||||
}
|
||||
|
||||
// Mark the non-xtest package for augmentation with
|
||||
|
@ -450,14 +459,12 @@ func (conf *Config) Load() (*Program, error) {
|
|||
prog.Imported[path] = info
|
||||
}
|
||||
|
||||
for _, files := range conf.CreatePkgs {
|
||||
pkgname, err := packageName(files, conf.Fset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, create := range conf.CreatePkgs {
|
||||
path := create.Path
|
||||
if create.Path == "" && len(create.Files) > 0 {
|
||||
path = create.Files[0].Name.Name
|
||||
}
|
||||
// TODO(adonovan): pkgnames are not unique, but the
|
||||
// typechecker assumes they are in its Id() logic.
|
||||
prog.Created = append(prog.Created, imp.createPackage(pkgname, files...))
|
||||
prog.Created = append(prog.Created, imp.createPackage(path, create.Files...))
|
||||
}
|
||||
|
||||
if len(prog.Imported)+len(prog.Created) == 0 {
|
||||
|
|
|
@ -93,7 +93,7 @@ func TestEnclosingFunction(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
conf.CreateFromFiles(f)
|
||||
conf.CreateFromFiles("main", f)
|
||||
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
|
|
|
@ -4,11 +4,7 @@
|
|||
|
||||
package loader
|
||||
|
||||
// This file defines various utility functions exposed by the package
|
||||
// and used by it.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
|
@ -104,25 +100,6 @@ func unreachable() {
|
|||
panic("unreachable")
|
||||
}
|
||||
|
||||
func packageName(files []*ast.File, fset *token.FileSet) (string, error) {
|
||||
if len(files) == 0 {
|
||||
return "", fmt.Errorf("no files in package")
|
||||
}
|
||||
// Take the package name from the 'package decl' in each file,
|
||||
// all of which must match.
|
||||
pkgname := files[0].Name.Name
|
||||
for _, file := range files[1:] {
|
||||
if pn := file.Name.Name; pn != pkgname {
|
||||
err := fmt.Errorf("can't load package: found packages %s (%s) and %s (%s)",
|
||||
pkgname, filename(files[0], fset),
|
||||
pn, filename(file, fset))
|
||||
return "", err
|
||||
}
|
||||
// TODO(adonovan): check dirnames are equal, like 'go build' does.
|
||||
}
|
||||
return pkgname, nil
|
||||
}
|
||||
|
||||
// TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
|
||||
func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
|
||||
p := int(pos)
|
||||
|
|
|
@ -49,7 +49,7 @@ func main() {
|
|||
}
|
||||
|
||||
// Create single-file main package and import its dependencies.
|
||||
conf.CreateFromFiles(file)
|
||||
conf.CreateFromFiles("main", file)
|
||||
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
|
|
|
@ -161,7 +161,7 @@ func doOneInput(input, filename string) bool {
|
|||
}
|
||||
|
||||
// Create single-file main package and import its dependencies.
|
||||
conf.CreateFromFiles(f)
|
||||
conf.CreateFromFiles("main", f)
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
|
|
@ -11,7 +11,7 @@ import "testing"
|
|||
|
||||
func log(f func(*testing.T)) {
|
||||
// The PTS of f is the set of called tests. TestingQuux is not present.
|
||||
print(f) // @pointsto a.Test | a.TestFoo
|
||||
print(f) // @pointsto main.Test | main.TestFoo
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
|
@ -36,7 +36,7 @@ func ExampleBar() {
|
|||
}
|
||||
|
||||
// Excludes TestingQuux.
|
||||
// @calls testing.tRunner -> a.Test
|
||||
// @calls testing.tRunner -> a.TestFoo
|
||||
// @calls testing.runExample -> a.ExampleBar
|
||||
// @calls (*testing.B).runN -> a.BenchmarkFoo
|
||||
// @calls testing.tRunner -> main.Test
|
||||
// @calls testing.tRunner -> main.TestFoo
|
||||
// @calls testing.runExample -> main.ExampleBar
|
||||
// @calls (*testing.B).runN -> main.BenchmarkFoo
|
||||
|
|
|
@ -47,7 +47,7 @@ func main() {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
conf.CreateFromFiles(f)
|
||||
conf.CreateFromFiles("main", f)
|
||||
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
|
@ -212,7 +212,7 @@ func TestTypesWithMethodSets(t *testing.T) {
|
|||
t.Errorf("test %d: %s", i, err)
|
||||
continue
|
||||
}
|
||||
conf.CreateFromFiles(f)
|
||||
conf.CreateFromFiles("p", f)
|
||||
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
|
|
|
@ -50,7 +50,7 @@ func main() {
|
|||
}
|
||||
|
||||
// Create single-file main package.
|
||||
conf.CreateFromFiles(file)
|
||||
conf.CreateFromFiles("main", file)
|
||||
|
||||
// Load the main package and its dependencies.
|
||||
iprog, err := conf.Load()
|
||||
|
|
|
@ -171,7 +171,7 @@ func run(t *testing.T, dir, input string, success successPredicate) bool {
|
|||
conf := loader.Config{SourceImports: true}
|
||||
// TODO(adonovan): add the following packages' tests, which pass:
|
||||
// "flag", "unicode", "unicode/utf8", "testing", "log", "path".
|
||||
if err := conf.CreateFromFilenames(inputs...); err != nil {
|
||||
if err := conf.CreateFromFilenames("", inputs...); err != nil {
|
||||
t.Errorf("CreateFromFilenames(%s) failed: %s", inputs, err)
|
||||
return false
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ func TestTestmainPackage(t *testing.T) {
|
|||
// CreateTestMainPackage should return nil if there were no tests.
|
||||
func TestNullTestmainPackage(t *testing.T) {
|
||||
var conf loader.Config
|
||||
if err := conf.CreateFromFilenames("testdata/b_test.go"); err != nil {
|
||||
if err := conf.CreateFromFilenames("", "testdata/b_test.go"); err != nil {
|
||||
t.Fatalf("ParseFile failed: %s", err)
|
||||
}
|
||||
iprog, err := conf.Load()
|
||||
|
|
|
@ -30,7 +30,7 @@ func TestObjValueLookup(t *testing.T) {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
conf.CreateFromFiles(f)
|
||||
conf.CreateFromFiles("main", f)
|
||||
|
||||
// Maps each var Ident (represented "name:linenum") to the
|
||||
// kind of ssa.Value we expect (represented "Constant", "&Alloc").
|
||||
|
@ -194,7 +194,7 @@ func TestValueForExpr(t *testing.T) {
|
|||
t.Error(err)
|
||||
return
|
||||
}
|
||||
conf.CreateFromFiles(f)
|
||||
conf.CreateFromFiles("main", f)
|
||||
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
|
|
|
@ -22,7 +22,7 @@ func TestSwitches(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
conf.CreateFromFiles(f)
|
||||
conf.CreateFromFiles("main", f)
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
|
@ -261,7 +261,7 @@ func TestMultipleQueries(t *testing.T) {
|
|||
buildContext.GOPATH = "testdata"
|
||||
conf := loader.Config{Build: &buildContext}
|
||||
filename := "testdata/src/main/multi.go"
|
||||
conf.CreateFromFilenames(filename)
|
||||
conf.CreateFromFilenames("", filename)
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load failed: %s", err)
|
||||
|
|
Loading…
Reference in New Issue