diff --git a/go/loader/loader.go b/go/loader/loader.go index 709bd20d..cf5d914e 100644 --- a/go/loader/loader.go +++ b/go/loader/loader.go @@ -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 _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 { diff --git a/go/loader/source_test.go b/go/loader/source_test.go index 8a63ea51..1c5653ec 100644 --- a/go/loader/source_test.go +++ b/go/loader/source_test.go @@ -93,7 +93,7 @@ func TestEnclosingFunction(t *testing.T) { continue } - conf.CreateFromFiles(f) + conf.CreateFromFiles("main", f) iprog, err := conf.Load() if err != nil { diff --git a/go/loader/util.go b/go/loader/util.go index e17c8a42..164ee66a 100644 --- a/go/loader/util.go +++ b/go/loader/util.go @@ -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) diff --git a/go/pointer/example_test.go b/go/pointer/example_test.go index db625783..07ec3a8c 100644 --- a/go/pointer/example_test.go +++ b/go/pointer/example_test.go @@ -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 { diff --git a/go/pointer/pointer_test.go b/go/pointer/pointer_test.go index daf8c184..6e00f6f1 100644 --- a/go/pointer/pointer_test.go +++ b/go/pointer/pointer_test.go @@ -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) diff --git a/go/pointer/testdata/a_test.go b/go/pointer/testdata/a_test.go index 828dcb6f..3baa9ac7 100644 --- a/go/pointer/testdata/a_test.go +++ b/go/pointer/testdata/a_test.go @@ -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 diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go index 6421cb87..03d77a04 100644 --- a/go/ssa/builder_test.go +++ b/go/ssa/builder_test.go @@ -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 { diff --git a/go/ssa/example_test.go b/go/ssa/example_test.go index d294ce2a..985a5219 100644 --- a/go/ssa/example_test.go +++ b/go/ssa/example_test.go @@ -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() diff --git a/go/ssa/interp/interp_test.go b/go/ssa/interp/interp_test.go index 4c1d4352..60dc9c3e 100644 --- a/go/ssa/interp/interp_test.go +++ b/go/ssa/interp/interp_test.go @@ -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() diff --git a/go/ssa/source_test.go b/go/ssa/source_test.go index 54155044..0f37f843 100644 --- a/go/ssa/source_test.go +++ b/go/ssa/source_test.go @@ -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 { diff --git a/go/ssa/ssautil/switch_test.go b/go/ssa/ssautil/switch_test.go index 5a4b38ff..a68f4644 100644 --- a/go/ssa/ssautil/switch_test.go +++ b/go/ssa/ssautil/switch_test.go @@ -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) diff --git a/oracle/oracle_test.go b/oracle/oracle_test.go index 37355646..19b4edb3 100644 --- a/oracle/oracle_test.go +++ b/oracle/oracle_test.go @@ -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)