func main() { flag.Parse() importPaths := gotool.ImportPaths(flag.Args()) if len(importPaths) == 0 { return } var conf loader.Config conf.Fset = fset for _, importPath := range importPaths { conf.Import(importPath) } prog, err := conf.Load() if err != nil { log.Fatal(err) } for _, pkg := range prog.InitialPackages() { for _, file := range pkg.Files { ast.Inspect(file, func(node ast.Node) bool { if s, ok := node.(*ast.StructType); ok { malign(node.Pos(), pkg.Types[s].Type.(*types.Struct)) } return true }) } } }
func TestCwd(t *testing.T) { ctxt := fakeContext(map[string]string{"one/two/three": `package three`}) for _, test := range []struct { cwd, arg, want string }{ {cwd: "/go/src/one", arg: "./two/three", want: "one/two/three"}, {cwd: "/go/src/one", arg: "../one/two/three", want: "one/two/three"}, {cwd: "/go/src/one", arg: "one/two/three", want: "one/two/three"}, {cwd: "/go/src/one/two/three", arg: ".", want: "one/two/three"}, {cwd: "/go/src/one", arg: "two/three", want: ""}, } { conf := loader.Config{ Cwd: test.cwd, Build: ctxt, } conf.Import(test.arg) var got string prog, err := conf.Load() if prog != nil { got = imported(prog) } if got != test.want { t.Errorf("Load(%s) from %s: Imported = %s, want %s", test.arg, test.cwd, got, test.want) if err != nil { t.Errorf("Load failed: %v", err) } } } }
func main() { // Loader is a tool for opening Go files, it loads from a Config type conf := loader.Config{ Build: &build.Default, } path, _ := filepath.Abs("looper") file, err := conf.ParseFile(path+"/"+"looper.go", nil) if err != nil { fmt.Println(err) return } // Creation of a single file main pacakge conf.CreateFromFiles("looper", file) conf.Import("runtime") p, err := conf.Load() if err != nil { fmt.Println(err) return } // Finally, create SSA representation from the package we've loaded program := ssautil.CreateProgram(p, ssa.SanityCheckFunctions) looperPkg := program.Package(p.Created[0].Pkg) fmt.Println("RIGHT IN THE SINGLE STATIC ASSIGNMENT FORM:") looperPkg.WriteTo(os.Stdout) fmt.Println("LOOK AT THIS HERE LOOPER FUNC:") looperFunc := looperPkg.Func("Looper") looperFunc.WriteTo(os.Stdout) }
// This example imports three packages, including the tests for one of // them, and loads all their dependencies. func ExampleConfig_Import() { // ImportWithTest("strconv") causes strconv to include // internal_test.go, and creates an external test package, // strconv_test. // (Compare with the example of CreateFromFiles.) var conf loader.Config conf.Import("unicode/utf8") conf.Import("errors") conf.ImportWithTests("strconv") prog, err := conf.Load() if err != nil { log.Fatal(err) } printProgram(prog) printFilenames(prog.Fset, prog.Package("strconv")) printFilenames(prog.Fset, prog.Package("strconv_test")) // Output: // created: [strconv_test] // imported: [errors strconv unicode/utf8] // initial: [errors strconv strconv_test unicode/utf8] // all: [bufio bytes errors flag fmt io log math math/rand os reflect runtime runtime/pprof runtime/trace sort strconv strconv_test strings sync sync/atomic syscall testing text/tabwriter time unicode unicode/utf8] // strconv.Files: [atob.go atof.go atoi.go decimal.go doc.go extfloat.go ftoa.go isprint.go itoa.go quote.go internal_test.go] // strconv_test.Files: [atob_test.go atof_test.go atoi_test.go decimal_test.go example_test.go fp_test.go ftoa_test.go itoa_test.go quote_test.go strconv_test.go] }
func TestTransitivelyErrorFreeFlag(t *testing.T) { // Create an minimal custom build.Context // that fakes the following packages: // // a --> b --> c! c has an error // \ d and e are transitively error-free. // e --> d // // Each package [a-e] consists of one file, x.go. pkgs := map[string]string{ "a": `package a; import (_ "b"; _ "e")`, "b": `package b; import _ "c"`, "c": `package c; func f() { _ = int(false) }`, // type error within function body "d": `package d;`, "e": `package e; import _ "d"`, } conf := loader.Config{ AllowErrors: true, SourceImports: true, Build: fakeContext(pkgs), } conf.Import("a") prog, err := conf.Load() if err != nil { t.Errorf("Load failed: %s", err) } if prog == nil { t.Fatalf("Load returned nil *Program") } for pkg, info := range prog.AllPackages { var wantErr, wantTEF bool switch pkg.Path() { case "a", "b": case "c": wantErr = true case "d", "e": wantTEF = true default: t.Errorf("unexpected package: %q", pkg.Path()) continue } if (info.Errors != nil) != wantErr { if wantErr { t.Errorf("Package %q.Error = nil, want error", pkg.Path()) } else { t.Errorf("Package %q has unexpected Errors: %v", pkg.Path(), info.Errors) } } if info.TransitivelyErrorFree != wantTEF { t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t", pkg.Path(), info.TransitivelyErrorFree, wantTEF) } } }
func TestLoad_BadDependency_AllowErrors(t *testing.T) { for _, test := range []struct { descr string pkgs map[string]string wantPkgs string }{ { descr: "missing dependency", pkgs: map[string]string{ "a": `package a; import _ "b"`, "b": `package b; import _ "c"`, }, wantPkgs: "a b", }, { descr: "bad package decl in dependency", pkgs: map[string]string{ "a": `package a; import _ "b"`, "b": `package b; import _ "c"`, "c": `package`, }, wantPkgs: "a b", }, { descr: "parse error in dependency", pkgs: map[string]string{ "a": `package a; import _ "b"`, "b": `package b; import _ "c"`, "c": `package c; var x = `, }, wantPkgs: "a b c", }, } { conf := loader.Config{ AllowErrors: true, SourceImports: true, Build: fakeContext(test.pkgs), } conf.Import("a") prog, err := conf.Load() if err != nil { t.Errorf("%s: Load failed unexpectedly: %v", test.descr, err) } if prog == nil { t.Fatalf("%s: Load returned a nil Program", test.descr) } if got, want := imported(prog), "a"; got != want { t.Errorf("%s: Imported = %s, want %s", test.descr, got, want) } if got := all(prog); strings.Join(got, " ") != test.wantPkgs { t.Errorf("%s: AllPackages = %s, want %s", test.descr, got, test.wantPkgs) } } }
func invalidProgram(name string) *loader.Program { var ldr loader.Config ldr.ParserMode = goparser.ParseComments ldr.Import("../fixtures/goparsing/" + name) prog, err := ldr.Load() if err != nil { log.Fatal(err) } return prog }
// Test that both syntax (scan/parse) and type errors are both recorded // (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error). func TestErrorReporting(t *testing.T) { pkgs := map[string]string{ "a": `package a; import _ "b"; var x int = false`, "b": `package b; 'syntax error!`, } conf := loader.Config{ AllowErrors: true, SourceImports: true, Build: fakeContext(pkgs), } var allErrors []error conf.TypeChecker.Error = func(err error) { allErrors = append(allErrors, err) } conf.Import("a") prog, err := conf.Load() if err != nil { t.Errorf("Load failed: %s", err) } if prog == nil { t.Fatalf("Load returned nil *Program") } hasError := func(errors []error, substr string) bool { for _, err := range errors { if strings.Contains(err.Error(), substr) { return true } } return false } // TODO(adonovan): test keys of ImportMap. // Check errors recorded in each PackageInfo. for pkg, info := range prog.AllPackages { switch pkg.Path() { case "a": if !hasError(info.Errors, "cannot convert false") { t.Errorf("a.Errors = %v, want bool conversion (type) error", info.Errors) } case "b": if !hasError(info.Errors, "rune literal not terminated") { t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors) } } } // Check errors reported via error handler. if !hasError(allErrors, "cannot convert false") || !hasError(allErrors, "rune literal not terminated") { t.Errorf("allErrors = %v, want both syntax and type errors", allErrors) } }
func loadProgram(ctx *build.Context, pkgs []string) (*loader.Program, error) { conf := loader.Config{ Build: ctx, ParserMode: parser.ParseComments, AllowErrors: false, } for _, pkg := range pkgs { conf.Import(pkg) } return conf.Load() }
// Test that syntax (scan/parse), type, and loader errors are recorded // (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error). func TestErrorReporting(t *testing.T) { pkgs := map[string]string{ "a": `package a; import (_ "b"; _ "c"); var x int = false`, "b": `package b; 'syntax error!`, } conf := loader.Config{ AllowErrors: true, Build: fakeContext(pkgs), } var mu sync.Mutex var allErrors []error conf.TypeChecker.Error = func(err error) { mu.Lock() allErrors = append(allErrors, err) mu.Unlock() } conf.Import("a") prog, err := conf.Load() if err != nil { t.Errorf("Load failed: %s", err) } if prog == nil { t.Fatalf("Load returned nil *Program") } // TODO(adonovan): test keys of ImportMap. // Check errors recorded in each PackageInfo. for pkg, info := range prog.AllPackages { switch pkg.Path() { case "a": if !hasError(info.Errors, "cannot convert false") { t.Errorf("a.Errors = %v, want bool conversion (type) error", info.Errors) } if !hasError(info.Errors, "could not import c") { t.Errorf("a.Errors = %v, want import (loader) error", info.Errors) } case "b": if !hasError(info.Errors, "rune literal not terminated") { t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors) } } } // Check errors reported via error handler. if !hasError(allErrors, "cannot convert false") || !hasError(allErrors, "rune literal not terminated") || !hasError(allErrors, "could not import c") { t.Errorf("allErrors = %v, want syntax, type and loader errors", allErrors) } }
// InlineDotImports displays Go package source code with dot imports inlined. func InlineDotImports(w io.Writer, importPath string) { /*imp2 := importer.New() imp2.Config.UseGcFallback = true cfg := types.Config{Import: imp2.Import} _ = cfg*/ conf := loader.Config{ //TypeChecker: cfg, } conf.Import(importPath) prog, err := conf.Load() if err != nil { panic(err) } /*pi, err := imp.ImportPackage(importPath) if err != nil { panic(err) } _ = pi*/ pi := prog.Imported[importPath] findDotImports(prog, pi) files := make(map[string]*ast.File) { // This package for _, file := range pi.Files { filename := prog.Fset.File(file.Package).Name() files[filename] = file } // All dot imports for _, pi := range dotImports { for _, file := range pi.Files { filename := prog.Fset.File(file.Package).Name() files[filename] = file } } } apkg := &ast.Package{Name: pi.Pkg.Name(), Files: files} merged := ast.MergePackageFiles(apkg, astMergeMode) WriteMergedPackage(w, prog.Fset, merged) }
func TestLoad_MissingInitialPackage(t *testing.T) { var conf loader.Config conf.Import("nosuchpkg") conf.Import("errors") const wantErr = "couldn't load packages due to errors: nosuchpkg" prog, err := conf.Load() if err == nil { t.Errorf("Load succeeded unexpectedly, want %q", wantErr) } else if err.Error() != wantErr { t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) } if prog != nil { t.Errorf("Load unexpectedly returned a Program") } }
func main() { if len(os.Args) != 3 { log.Fatal("Usage: doc <package> <object>") } //!+part1 pkgpath, name := os.Args[1], os.Args[2] // The loader loads a complete Go program from source code. conf := loader.Config{ParserMode: parser.ParseComments} conf.Import(pkgpath) lprog, err := conf.Load() if err != nil { log.Fatal(err) // load error } // Find the package and package-level object. pkg := lprog.Package(pkgpath).Pkg obj := pkg.Scope().Lookup(name) if obj == nil { log.Fatalf("%s.%s not found", pkg.Path(), name) } //!-part1 //!+part2 // Print the object and its methods (incl. location of definition). fmt.Println(obj) for _, sel := range typeutil.IntuitiveMethodSet(obj.Type(), nil) { fmt.Printf("%s: %s\n", lprog.Fset.Position(sel.Obj().Pos()), sel) } // Find the path from the root of the AST to the object's position. // Walk up to the enclosing ast.Decl for the doc comment. _, path, _ := lprog.PathEnclosingInterval(obj.Pos(), obj.Pos()) for _, n := range path { switch n := n.(type) { case *ast.GenDecl: fmt.Println("\n", n.Doc.Text()) return case *ast.FuncDecl: fmt.Println("\n", n.Doc.Text()) return } } //!-part2 }
// importQueryPackage finds the package P containing the // query position and tells conf to import it. // It returns the package's path. func importQueryPackage(pos string, conf *loader.Config) (string, error) { fqpos, err := fastQueryPos(pos) if err != nil { return "", err // bad query } filename := fqpos.fset.File(fqpos.start).Name() // This will not work for ad-hoc packages // such as $GOROOT/src/net/http/triv.go. // TODO(adonovan): ensure we report a clear error. _, importPath, err := guessImportPath(filename, conf.Build) if err != nil { return "", err // can't find GOPATH dir } if importPath == "" { return "", fmt.Errorf("can't guess import path from %s", filename) } // Check that it's possible to load the queried package. // (e.g. oracle tests contain different 'package' decls in same dir.) // Keep consistent with logic in loader/util.go! cfg2 := *conf.Build cfg2.CgoEnabled = false bp, err := cfg2.Import(importPath, "", 0) if err != nil { return "", err // no files for package } switch pkgContainsFile(bp, filename) { case 'T': conf.ImportWithTests(importPath) case 'X': conf.ImportWithTests(importPath) importPath += "_test" // for TypeCheckFuncBodies case 'G': conf.Import(importPath) default: return "", fmt.Errorf("package %q doesn't contain file %s", importPath, filename) } conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath } return importPath, nil }
// This program shows how to load a main package (cmd/cover) and all its // dependencies from source, using the loader, and then build SSA code // for the entire program. This is what you'd typically use for a // whole-program analysis. // func ExampleLoadProgram() { // Load cmd/cover and its dependencies. var conf loader.Config conf.Import("cmd/cover") lprog, err := conf.Load() if err != nil { fmt.Print(err) // type error in some package return } // Create SSA-form program representation. prog := ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions) // Build SSA code for the entire cmd/cover program. prog.Build() // Output: }
func main() { if len(os.Args) != 4 { log.Fatal(usage) } pkgpath, ifacename, concname := os.Args[1], os.Args[2], os.Args[3] // The loader loads a complete Go program from source code. var conf loader.Config conf.Import(pkgpath) lprog, err := conf.Load() if err != nil { log.Fatal(err) // load error } pkg := lprog.Package(pkgpath).Pkg if err := PrintSkeleton(pkg, ifacename, concname); err != nil { log.Fatal(err) } }
// importQueryPackage finds the package P containing the // query position and tells conf to import it. // It returns the package's path. func importQueryPackage(pos string, conf *loader.Config) (string, error) { fqpos, err := fastQueryPos(conf.Build, pos) if err != nil { return "", err // bad query } filename := fqpos.fset.File(fqpos.start).Name() _, importPath, err := guessImportPath(filename, conf.Build) if err != nil { // Can't find GOPATH dir. // Treat the query file as its own package. importPath = "command-line-arguments" conf.CreateFromFilenames(importPath, filename) } else { // Check that it's possible to load the queried package. // (e.g. guru tests contain different 'package' decls in same dir.) // Keep consistent with logic in loader/util.go! cfg2 := *conf.Build cfg2.CgoEnabled = false bp, err := cfg2.Import(importPath, "", 0) if err != nil { return "", err // no files for package } switch pkgContainsFile(bp, filename) { case 'T': conf.ImportWithTests(importPath) case 'X': conf.ImportWithTests(importPath) importPath += "_test" // for TypeCheckFuncBodies case 'G': conf.Import(importPath) default: // This happens for ad-hoc packages like // $GOROOT/src/net/http/triv.go. return "", fmt.Errorf("package %q doesn't contain file %s", importPath, filename) } } conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath } return importPath, nil }
func Grapher(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { repo := ps.ByName("importpath") repo = strings.TrimLeft(repo, "/") log.Println(repo) _, err := exec.Command("go", "get", "-u", "-f", repo).Output() if err != nil { w.Write([]byte("couldn't get repo [" + repo + "]:" + err.Error())) return } var conf loader.Config conf.Import(repo) prog, err := conf.Load() if err != nil { log.Fatal(err) } ssaProg := ssautil.CreateProgram(prog, 0) ssaProg.Build() var nodes []node.Node nodes, err = rta.GetNodes(ssaProg) if err != nil { nodes, err = cha.GetNodes(ssaProg) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } by, err := json.Marshal(nodes) if err != nil { panic(err) } var data = struct { Graph string }{ Graph: string(by), } tmpl, _ := template.New("foo").Parse(nt) tmpl.Execute(w, data) }
func TestVendorCwd(t *testing.T) { if buildutil.AllowVendor == 0 { // Vendoring requires Go 1.6. // TODO(adonovan): delete in due course. t.Skip() } // Test the interaction of cwd and vendor directories. ctxt := fakeContext(map[string]string{ "net": ``, // mkdir net "net/http": `package http; import _ "hpack"`, "vendor": ``, // mkdir vendor "vendor/hpack": `package vendorhpack`, "hpack": `package hpack`, }) for i, test := range []struct { cwd, arg, want string }{ {cwd: "/go/src/net", arg: "http"}, // not found {cwd: "/go/src/net", arg: "./http", want: "net/http vendor/hpack"}, {cwd: "/go/src/net", arg: "hpack", want: "hpack"}, {cwd: "/go/src/vendor", arg: "hpack", want: "hpack"}, {cwd: "/go/src/vendor", arg: "./hpack", want: "vendor/hpack"}, } { conf := loader.Config{ Cwd: test.cwd, Build: ctxt, } conf.Import(test.arg) var got string prog, err := conf.Load() if prog != nil { got = strings.Join(all(prog), " ") } if got != test.want { t.Errorf("#%d: Load(%s) from %s: got %s, want %s", i, test.arg, test.cwd, got, test.want) if err != nil { t.Errorf("Load failed: %v", err) } } } }
func TestLoad_MissingInitialPackage_AllowErrors(t *testing.T) { var conf loader.Config conf.AllowErrors = true conf.Import("nosuchpkg") conf.ImportWithTests("errors") prog, err := conf.Load() if err != nil { t.Errorf("Load failed unexpectedly: %v", err) } if prog == nil { t.Fatalf("Load returned a nil Program") } if got, want := created(prog), "errors_test"; got != want { t.Errorf("Created = %s, want %s", got, want) } if got, want := imported(prog), "errors"; got != want { t.Errorf("Imported = %s, want %s", got, want) } }
func TestLoad_MissingIndirectImport(t *testing.T) { pkgs := map[string]string{ "a": `package a; import _ "b"`, "b": `package b; import _ "c"`, } conf := loader.Config{Build: fakeContext(pkgs)} conf.Import("a") const wantErr = "couldn't load packages due to errors: b" prog, err := conf.Load() if err == nil { t.Errorf("Load succeeded unexpectedly, want %q", wantErr) } else if err.Error() != wantErr { t.Errorf("Load failed with wrong error %q, want %q", err, wantErr) } if prog != nil { t.Errorf("Load unexpectedly returned a Program") } }
func main() { flag.Parse() var conf loader.Config conf.Fset = fset for _, arg := range flag.Args() { conf.Import(arg) } prog, err := conf.Load() if err != nil { log.Fatal(err) } var v visitor for _, pkg := range prog.InitialPackages() { v.pkg = pkg for _, file := range pkg.Files { ast.Walk(&v, file) } } }
func importPackage(dir string) *importData { installPackage() buildPkg, err := build.ImportDir(dir, 0) if err != nil { log.Fatalf(`build.ImportDir(%q, 0): %s`, dir, err) } // load package var conf loader.Config conf.Import(buildPkg.ImportPath) prog, err := conf.Load() if err != nil { log.Fatalf("conf.Load(): %s", err) } // get our single package packages := make([]string, 0, len(prog.Imported)) for p := range prog.Imported { packages = append(packages, p) } if len(packages) != 1 { log.Fatalf("expected 1 package, got %d: %v", len(packages), packages) } pack := prog.Imported[packages[0]].Pkg // TODO compare pack and buildPkg tests := extractTestFunctions(pack.Scope()) return &importData{ PackageName: pack.Name(), PackageImportPath: buildPkg.ImportPath, PackageDir: buildPkg.Dir, Tests: tests, } }
// newAppScanner creates a new api parser func newAppScanner(bp string, input *spec.Swagger, includes, excludes packageFilters) (*appScanner, error) { var ldr loader.Config ldr.ParserMode = goparser.ParseComments ldr.Import(bp) prog, err := ldr.Load() if err != nil { return nil, err } if input == nil { input = new(spec.Swagger) input.Swagger = "2.0" } if input.Paths == nil { input.Paths = new(spec.Paths) } if input.Definitions == nil { input.Definitions = make(map[string]spec.Schema) } if input.Responses == nil { input.Responses = make(map[string]spec.Response) } return &appScanner{ MainPackage: bp, prog: prog, input: input, loader: &ldr, operations: collectOperationsFromInput(input), definitions: input.Definitions, responses: input.Responses, classifier: &programClassifier{ Includes: includes, Excludes: excludes, }, }, nil }
func TestLoad_vendor(t *testing.T) { if buildutil.AllowVendor == 0 { // Vendoring requires Go 1.6. // TODO(adonovan): delete in due course. t.Skip() } pkgs := map[string]string{ "a": `package a; import _ "x"`, "a/vendor": ``, // mkdir a/vendor "a/vendor/x": `package xa`, "b": `package b; import _ "x"`, "b/vendor": ``, // mkdir b/vendor "b/vendor/x": `package xb`, "c": `package c; import _ "x"`, "x": `package xc`, } conf := loader.Config{Build: fakeContext(pkgs)} conf.Import("a") conf.Import("b") conf.Import("c") prog, err := conf.Load() if err != nil { t.Fatal(err) } // Check that a, b, and c see different versions of x. for _, r := range "abc" { name := string(r) got := prog.Package(name).Pkg.Imports()[0] want := "x" + name if got.Name() != want { t.Errorf("package %s import %q = %s, want %s", name, "x", got.Name(), want) } } }
func classifierProgram() *loader.Program { var ldr loader.Config ldr.ParserMode = goparser.ParseComments ldr.Import("../fixtures/goparsing/classification") ldr.Import("../fixtures/goparsing/classification/models") ldr.Import("../fixtures/goparsing/classification/operations") prog, err := ldr.Load() if err != nil { log.Fatal(err) } return prog }
func bundle(w io.Writer, initialPkg, dest, prefix string) error { // Load the initial package. conf := loader.Config{ParserMode: parser.ParseComments, Build: ctxt} conf.TypeCheckFuncBodies = func(p string) bool { return p == initialPkg } conf.Import(initialPkg) lprog, err := conf.Load() if err != nil { log.Fatal(err) } info := lprog.Package(initialPkg) objsToUpdate := make(map[types.Object]bool) var rename func(from types.Object) rename = func(from types.Object) { if !objsToUpdate[from] { objsToUpdate[from] = true // Renaming a type that is used as an embedded field // requires renaming the field too. e.g. // type T int // if we rename this to U.. // var s struct {T} // print(s.T) // ...this must change too if _, ok := from.(*types.TypeName); ok { for id, obj := range info.Uses { if obj == from { if field := info.Defs[id]; field != nil { rename(field) } } } } } } // Rename each package-level object. scope := info.Pkg.Scope() for _, name := range scope.Names() { rename(scope.Lookup(name)) } var out bytes.Buffer fmt.Fprintf(&out, "// Code generated by golang.org/x/tools/cmd/bundle command:\n") fmt.Fprintf(&out, "// $ bundle %s %s %s\n\n", initialPkg, dest, prefix) // Concatenate package comments from of all files. for _, f := range info.Files { if doc := f.Doc.Text(); strings.TrimSpace(doc) != "" { for _, line := range strings.Split(doc, "\n") { fmt.Fprintf(&out, "// %s\n", line) } } } // TODO(adonovan): don't assume pkg.name == basename(pkg.path). fmt.Fprintf(&out, "package %s\n\n", filepath.Base(dest)) // Print a single declaration that imports all necessary packages. // TODO(adonovan): // - support renaming imports. // - preserve comments from the original import declarations. for _, f := range info.Files { for _, imp := range f.Imports { if imp.Name != nil { log.Fatalf("%s: renaming imports not supported", lprog.Fset.Position(imp.Pos())) } } } fmt.Fprintln(&out, "import (") for _, p := range info.Pkg.Imports() { if p.Path() == dest { continue } fmt.Fprintf(&out, "\t%q\n", p.Path()) } fmt.Fprintln(&out, ")\n") // Modify and print each file. for _, f := range info.Files { // Update renamed identifiers. for id, obj := range info.Defs { if objsToUpdate[obj] { id.Name = prefix + obj.Name() } } for id, obj := range info.Uses { if objsToUpdate[obj] { id.Name = prefix + obj.Name() } } // For each qualified identifier that refers to the // destination package, remove the qualifier. // The "@@@." strings are removed in postprocessing. ast.Inspect(f, func(n ast.Node) bool { if sel, ok := n.(*ast.SelectorExpr); ok { if id, ok := sel.X.(*ast.Ident); ok { if obj, ok := info.Uses[id].(*types.PkgName); ok { if obj.Imported().Path() == dest { id.Name = "@@@" } } } } return true }) // Pretty-print package-level declarations. // but no package or import declarations. // // TODO(adonovan): this may cause loss of comments // preceding or associated with the package or import // declarations or not associated with any declaration. // Check. var buf bytes.Buffer for _, decl := range f.Decls { if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT { continue } buf.Reset() format.Node(&buf, lprog.Fset, decl) // Remove each "@@@." in the output. // TODO(adonovan): not hygienic. out.Write(bytes.Replace(buf.Bytes(), []byte("@@@."), nil, -1)) out.WriteString("\n\n") } } // Now format the entire thing. result, err := format.Source(out.Bytes()) if err != nil { log.Fatalf("formatting failed: %v", err) } _, err = w.Write(result) return err }
func TestBExportData_stdlib(t *testing.T) { if runtime.GOOS == "android" { t.Skipf("incomplete std lib on %s", runtime.GOOS) } // Load, parse and type-check the program. ctxt := build.Default // copy ctxt.GOPATH = "" // disable GOPATH conf := loader.Config{ Build: &ctxt, AllowErrors: true, } for _, path := range buildutil.AllPackages(conf.Build) { conf.Import(path) } // Create a package containing type and value errors to ensure // they are properly encoded/decoded. f, err := conf.ParseFile("haserrors/haserrors.go", `package haserrors const UnknownValue = "" + 0 type UnknownType undefined `) if err != nil { t.Fatal(err) } conf.CreateFromFiles("haserrors", f) prog, err := conf.Load() if err != nil { t.Fatalf("Load failed: %v", err) } numPkgs := len(prog.AllPackages) if want := 248; numPkgs < want { t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want) } for pkg, info := range prog.AllPackages { if info.Files == nil { continue // empty directory } exportdata := gcimporter.BExportData(conf.Fset, pkg) imports := make(map[string]*types.Package) fset2 := token.NewFileSet() n, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg.Path()) if err != nil { t.Errorf("BImportData(%s): %v", pkg.Path(), err) continue } if n != len(exportdata) { t.Errorf("BImportData(%s) decoded %d bytes, want %d", pkg.Path(), n, len(exportdata)) } // Compare the packages' corresponding members. for _, name := range pkg.Scope().Names() { if !ast.IsExported(name) { continue } obj1 := pkg.Scope().Lookup(name) obj2 := pkg2.Scope().Lookup(name) if obj2 == nil { t.Errorf("%s.%s not found, want %s", pkg.Path(), name, obj1) continue } fl1 := fileLine(conf.Fset, obj1) fl2 := fileLine(fset2, obj2) if fl1 != fl2 { t.Errorf("%s.%s: got posn %s, want %s", pkg.Path(), name, fl2, fl1) } if err := equalObj(obj1, obj2); err != nil { t.Errorf("%s.%s: %s\ngot: %s\nwant: %s", pkg.Path(), name, err, obj2, obj1) } } } }
func run(t *testing.T, dir, input string, success successPredicate) bool { fmt.Printf("Input: %s\n", input) start := time.Now() var inputs []string for _, i := range strings.Split(input, " ") { if strings.HasSuffix(i, ".go") { i = dir + i } inputs = append(inputs, i) } var conf loader.Config if _, err := conf.FromArgs(inputs, true); err != nil { t.Errorf("FromArgs(%s) failed: %s", inputs, err) return false } conf.Import("runtime") // Print a helpful hint if we don't make it to the end. var hint string defer func() { if hint != "" { fmt.Println("FAIL") fmt.Println(hint) } else { fmt.Println("PASS") } interp.CapturedOutput = nil }() hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input) iprog, err := conf.Load() if err != nil { t.Errorf("conf.Load(%s) failed: %s", inputs, err) return false } prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions) prog.BuildAll() var mainPkg *ssa.Package var initialPkgs []*ssa.Package for _, info := range iprog.InitialPackages() { if info.Pkg.Path() == "runtime" { continue // not an initial package } p := prog.Package(info.Pkg) initialPkgs = append(initialPkgs, p) if mainPkg == nil && p.Func("main") != nil { mainPkg = p } } if mainPkg == nil { testmainPkg := prog.CreateTestMainPackage(initialPkgs...) if testmainPkg == nil { t.Errorf("CreateTestMainPackage(%s) returned nil", mainPkg) return false } if testmainPkg.Func("main") == nil { t.Errorf("synthetic testmain package has no main") return false } mainPkg = testmainPkg } var out bytes.Buffer interp.CapturedOutput = &out hint = fmt.Sprintf("To trace execution, run:\n%% go build golang.org/x/tools/cmd/ssadump && ./ssadump -build=C -run --interp=T %s\n", input) exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{8, 8}, inputs[0], []string{}) // The definition of success varies with each file. if err := success(exitCode, out.String()); err != nil { t.Errorf("interp.Interpret(%s) failed: %s", inputs, err) return false } hint = "" // call off the hounds if false { fmt.Println(input, time.Since(start)) // test profiling } return true }
func doMain() error { flag.Parse() args := flag.Args() conf := loader.Config{Build: &build.Default} // Choose types.Sizes from conf.Build. var wordSize int64 = 8 switch conf.Build.GOARCH { case "386", "arm": wordSize = 4 } conf.TypeChecker.Sizes = &types.StdSizes{ MaxAlign: 8, WordSize: wordSize, } var interpMode interp.Mode for _, c := range *interpFlag { switch c { case 'T': interpMode |= interp.EnableTracing case 'R': interpMode |= interp.DisableRecover default: return fmt.Errorf("unknown -interp option: '%c'", c) } } if len(args) == 0 { fmt.Fprint(os.Stderr, usage) os.Exit(1) } // Profiling support. if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } // Use the initial packages from the command line. args, err := conf.FromArgs(args, *testFlag) if err != nil { return err } // The interpreter needs the runtime package. if *runFlag { conf.Import("runtime") } // Load, parse and type-check the whole program. iprog, err := conf.Load() if err != nil { return err } // Create and build SSA-form program representation. prog := ssautil.CreateProgram(iprog, *modeFlag) // Build and display only the initial packages // (and synthetic wrappers), unless -run is specified. for _, info := range iprog.InitialPackages() { prog.Package(info.Pkg).Build() } // Run the interpreter. if *runFlag { prog.Build() var main *ssa.Package pkgs := prog.AllPackages() if *testFlag { // If -test, run all packages' tests. if len(pkgs) > 0 { main = prog.CreateTestMainPackage(pkgs...) } if main == nil { return fmt.Errorf("no tests") } } else { // Otherwise, run main.main. for _, pkg := range pkgs { if pkg.Pkg.Name() == "main" { main = pkg if main.Func("main") == nil { return fmt.Errorf("no func main() in main package") } break } } if main == nil { return fmt.Errorf("no main package") } } if runtime.GOARCH != build.Default.GOARCH { return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)", build.Default.GOARCH, runtime.GOARCH) } interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args) } return nil }