// Parse parses the ast for this file and returns a ParsedFile func (w *Weave) ParseAST(fname string) *ast.File { var err error fset := token.NewFileSet() af, err := parser.ParseFile(fset, fname, nil, 0) if err != nil { w.flog.Println(err) } loadcfg := loader.Config{} loadcfg.CreateFromFilenames(fname) info := types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), } var conf types.Config _, err = conf.Check(af.Name.Name, fset, []*ast.File{af}, &info) if err != nil { if w.warnAST { w.flog.Println(err) } } return af }
// loadWithSoftErrors calls lconf.Load, suppressing "soft" errors. (See Go issue 16530.) // TODO(adonovan): Once the loader has an option to allow soft errors, // replace calls to loadWithSoftErrors with loader calls with that parameter. func loadWithSoftErrors(lconf *loader.Config) (*loader.Program, error) { lconf.AllowErrors = true // Ideally we would just return conf.Load() here, but go/types // reports certain "soft" errors that gc does not (Go issue 14596). // As a workaround, we set AllowErrors=true and then duplicate // the loader's error checking but allow soft errors. // It would be nice if the loader API permitted "AllowErrors: soft". prog, err := lconf.Load() if err != nil { return nil, err } var errpkgs []string // Report hard errors in indirectly imported packages. for _, info := range prog.AllPackages { if containsHardErrors(info.Errors) { errpkgs = append(errpkgs, info.Pkg.Path()) } else { // Enable SSA construction for packages containing only soft errors. info.TransitivelyErrorFree = true } } if errpkgs != nil { var more string if len(errpkgs) > 3 { more = fmt.Sprintf(" and %d more", len(errpkgs)-3) errpkgs = errpkgs[:3] } return nil, fmt.Errorf("couldn't load packages due to errors: %s%s", strings.Join(errpkgs, ", "), more) } return prog, err }
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 loadProgram(args []string, tests bool) (*ssa.Program, error) { conf := loader.Config{} if len(args) == 0 { fmt.Fprintln(os.Stderr, Usage) os.Exit(1) } // Use the initial packages from the command line. args, err := conf.FromArgs(args, tests) if err != nil { return nil, err } // Load, parse and type-check the whole program. iprog, err := conf.Load() if err != nil { return nil, err } // Create and build SSA-form program representation. prog := ssautil.CreateProgram(iprog, 0) prog.BuildAll() return prog, nil }
func TestStdlib(t *testing.T) { defer func(saved func(format string, args ...interface{})) { logf = saved }(logf) logf = t.Errorf ctxt := build.Default // copy // Enumerate $GOROOT packages. saved := ctxt.GOPATH ctxt.GOPATH = "" // disable GOPATH during AllPackages pkgs := buildutil.AllPackages(&ctxt) ctxt.GOPATH = saved // Throw in a number of go.tools packages too. pkgs = append(pkgs, "golang.org/x/tools/cmd/godoc", "golang.org/x/tools/refactor/lexical") // Load, parse and type-check the program. conf := loader.Config{Build: &ctxt} for _, path := range pkgs { conf.ImportWithTests(path) } iprog, err := conf.Load() if err != nil { t.Fatalf("Load failed: %v", err) } // This test ensures that Structure doesn't panic and that // its internal sanity-checks against go/types don't fail. for pkg, info := range iprog.AllPackages { _ = Structure(iprog.Fset, pkg, &info.Info, info.Files) } }
// Create unexporter func NewUnexporter(o *Config) (*unexporter, error) { var conf loader.Config for _, package_name := range buildutil.AllPackages(&build.Default) { conf.ImportWithTests(package_name) } program, err := conf.Load() if err != nil { return nil, err } if program.Package(o.Pkg) == nil { return nil, errors.New(fmt.Sprintf("'%s' is not a valid package", o.Pkg)) } if o.Debug { pkg := program.Package(o.Pkg).Pkg fmt.Printf("finding unused identifiers for %s (%s)\n", pkg.Name(), pkg.Path()) } unexporter := &unexporter{ Program: program, MainPackage: program.Package(o.Pkg), Verbose: o.Debug, } unexporter.run() return unexporter, nil }
// Return count of unused vars func myloader(args []string) (count int) { var conf loader.Config conf.FromArgs(args, false) prog, err := conf.Load() if err != nil { log.Fatal(err) } for _, pi := range prog.Created { fmt.Println(pi) } info := prog.Package(args[0]).Info fset := prog.Fset ssaprog := ssautil.CreateProgram(prog, ssa.GlobalDebug) ssaprog.Build() for expr, object := range info.Defs { unused := checkObj(expr, object, prog, ssaprog, fset) if unused { fmt.Fprintf(os.Stderr, "Unused assignment for '%v' %v\n", expr, fset.Position(expr.Pos())) count++ } } for expr, object := range info.Uses { unused := checkObj(expr, object, prog, ssaprog, fset) if unused { fmt.Fprintf(os.Stderr, "Unused assignment for '%v' %v\n", expr, fset.Position(expr.Pos())) count++ } } return count }
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) } } }
// createProgram returns a program containing packages specified. func createProgram(packages ...string) (*loader.Program, error) { var conf loader.Config for _, name := range packages { conf.CreateFromFilenames(name, getFileNames(name)...) } return conf.Load() }
// Query runs a single oracle query. // // args specify the main package in (*loader.Config).FromArgs syntax. // mode is the query mode ("callers", etc). // ptalog is the (optional) pointer-analysis log file. // buildContext is the go/build configuration for locating packages. // reflection determines whether to model reflection soundly (currently slow). // // Clients that intend to perform multiple queries against the same // analysis scope should use this pattern instead: // // conf := loader.Config{Build: buildContext, SourceImports: true} // ... populate config, e.g. conf.FromArgs(args) ... // iprog, err := conf.Load() // if err != nil { ... } // o, err := oracle.New(iprog, nil, false) // if err != nil { ... } // for ... { // qpos, err := oracle.ParseQueryPos(imp, pos, needExact) // if err != nil { ... } // // res, err := o.Query(mode, qpos) // if err != nil { ... } // // // use res // } // // TODO(adonovan): the ideal 'needsExact' parameter for ParseQueryPos // depends on the query mode; how should we expose this? // func Query(args []string, mode, pos string, ptalog io.Writer, buildContext *build.Context, reflection bool) (*Result, error) { if mode == "what" { // Bypass package loading, type checking, SSA construction. return what(pos, buildContext) } minfo := findMode(mode) if minfo == nil { return nil, fmt.Errorf("invalid mode type: %q", mode) } conf := loader.Config{Build: buildContext, SourceImports: true} // Determine initial packages. args, err := conf.FromArgs(args, true) if err != nil { return nil, err } if len(args) > 0 { return nil, fmt.Errorf("surplus arguments: %q", args) } // For queries needing only a single typed package, // reduce the analysis scope to that package. if minfo.needs&(needSSA|needRetainTypeInfo) == 0 { reduceScope(pos, &conf) } // TODO(adonovan): report type errors to the user via Serial // types, not stderr? // conf.TypeChecker.Error = func(err error) { // E := err.(types.Error) // fmt.Fprintf(os.Stderr, "%s: %s\n", E.Fset.Position(E.Pos), E.Msg) // } // Load/parse/type-check the program. iprog, err := conf.Load() if err != nil { return nil, err } o, err := newOracle(iprog, ptalog, minfo.needs, reflection) if err != nil { return nil, err } qpos, err := ParseQueryPos(iprog, pos, minfo.needs&needExactPos != 0) if err != nil && minfo.needs&(needPos|needExactPos) != 0 { return nil, err } // SSA is built and we have the QueryPos. // Release the other ASTs and type info to the GC. iprog = nil return o.query(minfo, qpos) }
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) } } }
// allowErrors causes type errors to be silently ignored. // (Not suitable if SSA construction follows.) func allowErrors(lconf *loader.Config) { ctxt := *lconf.Build // copy ctxt.CgoEnabled = false lconf.Build = &ctxt lconf.AllowErrors = true // AllErrors makes the parser always return an AST instead of // bailing out after 10 errors and returning an empty ast.File. lconf.ParserMode = parser.AllErrors lconf.TypeChecker.Error = func(err error) {} }
// 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) } }
// 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 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) }
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() }
func TestCreateUnnamedPackage(t *testing.T) { var conf loader.Config conf.CreateFromFilenames("") prog, err := conf.Load() if err != nil { t.Fatalf("Load failed: %v", err) } if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want { t.Errorf("InitialPackages = %s, want %s", got, want) } }
func TestLoad_MissingFileInCreatedPackage_AllowErrors(t *testing.T) { conf := loader.Config{AllowErrors: true} conf.CreateFromFilenames("", "missing.go") prog, err := conf.Load() if err != nil { t.Errorf("Load failed: %v", err) } if got, want := fmt.Sprint(prog.InitialPackages()), "[(unnamed)]"; got != want { t.Fatalf("InitialPackages = %s, want %s", got, 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 }
// Build constructs the SSA IR using given config, and sets up pointer analysis. func (conf *Config) Build() (*SSAInfo, error) { var lconf = loader.Config{Build: &build.Default} buildLog := log.New(conf.BuildLog, "ssabuild: ", conf.LogFlags) if conf.BuildMode == FromFiles { args, err := lconf.FromArgs(conf.Files, false /* No tests */) if err != nil { return nil, err } if len(args) > 0 { return nil, fmt.Errorf("surplus arguments: %q", args) } } else if conf.BuildMode == FromString { f, err := lconf.ParseFile("", conf.Source) if err != nil { return nil, err } lconf.CreateFromFiles("", f) } else { buildLog.Fatal("Unknown build mode") } // Load, parse and type-check program lprog, err := lconf.Load() if err != nil { return nil, err } buildLog.Print("Program loaded and type checked") prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug|ssa.BareInits) // Prepare Config for whole-program pointer analysis. ptaConf, err := setupPTA(prog, lprog, conf.PtaLog) ignoredPkgs := []string{} if len(conf.BadPkgs) == 0 { prog.Build() } else { for _, info := range lprog.AllPackages { if reason, badPkg := conf.BadPkgs[info.Pkg.Name()]; badPkg { buildLog.Printf("Skip package: %s (%s)", info.Pkg.Name(), reason) ignoredPkgs = append(ignoredPkgs, info.Pkg.Name()) } else { prog.Package(info.Pkg).Build() } } } return &SSAInfo{ BuildConf: conf, IgnoredPkgs: ignoredPkgs, FSet: lprog.Fset, Prog: prog, PtaConf: ptaConf, Logger: buildLog, }, nil }
// 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) } }
func genPkg(pkg *build.Package) { if len(pkg.CgoFiles) > 0 { errorf("gobind: cannot use cgo-dependent package as service definition: %s", pkg.CgoFiles[0]) return } files := parseFiles(pkg.Dir, pkg.GoFiles) if len(files) == 0 { return // some error has been reported } conf := loader.Config{ Fset: fset, } conf.TypeChecker.Error = func(err error) { errorf("%v", err) } conf.CreateFromFiles(pkg.ImportPath, files...) program, err := conf.Load() if err != nil { errorf("%v", err) return } p := program.Created[0].Pkg fname := defaultFileName(*lang, p) switch *lang { case "java": w, closer := writer(fname, p) processErr(bind.GenJava(w, fset, p)) closer() case "go": w, closer := writer(fname, p) processErr(bind.GenGo(w, fset, p)) closer() case "objc": if fname == "" { processErr(bind.GenObjc(os.Stdout, fset, p, true)) processErr(bind.GenObjc(os.Stdout, fset, p, false)) } else { hname := fname[:len(fname)-2] + ".h" w, closer := writer(hname, p) processErr(bind.GenObjc(w, fset, p, true)) closer() w, closer = writer(fname, p) processErr(bind.GenObjc(w, fset, p, false)) closer() } default: errorf("unknown target language: %q", *lang) } }
func newBinder(bindPkg *build.Package) (*binder, error) { if bindPkg.Name == "main" { return nil, fmt.Errorf("package %q: can only bind a library package", bindPkg.Name) } if len(bindPkg.CgoFiles) > 0 { return nil, fmt.Errorf("cannot use cgo-dependent package as service definition: %s", bindPkg.CgoFiles[0]) } fset := token.NewFileSet() hasErr := false var files []*ast.File for _, filename := range bindPkg.GoFiles { p := filepath.Join(bindPkg.Dir, filename) file, err := parser.ParseFile(fset, p, nil, parser.AllErrors) if err != nil { hasErr = true if list, _ := err.(scanner.ErrorList); len(list) > 0 { for _, err := range list { fmt.Fprintln(os.Stderr, err) } } else { fmt.Fprintln(os.Stderr, err) } } files = append(files, file) } if hasErr { return nil, errors.New("package parsing failed.") } conf := loader.Config{ Fset: fset, } conf.TypeChecker.Error = func(err error) { fmt.Fprintln(os.Stderr, err) } conf.CreateFromFiles(bindPkg.ImportPath, files...) program, err := conf.Load() if err != nil { return nil, err } b := &binder{ files: files, fset: fset, pkg: program.Created[0].Pkg, } return b, nil }
// describe describes the syntax node denoted by the query position, // including: // - its syntactic category // - the definition of its referent (for identifiers) [now redundant] // - its type, fields, and methods (for an expression or type expression) // func describe(q *Query) error { lconf := loader.Config{Build: q.Build} allowErrors(&lconf) if _, err := importQueryPackage(q.Pos, &lconf); err != nil { return err } // Load/parse/type-check the program. lprog, err := lconf.Load() if err != nil { return err } qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos) if err != nil { return err } if false { // debugging fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s", astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path)) } var qr QueryResult path, action := findInterestingNode(qpos.info, qpos.path) switch action { case actionExpr: qr, err = describeValue(qpos, path) case actionType: qr, err = describeType(qpos, path) case actionPackage: qr, err = describePackage(qpos, path) case actionStmt: qr, err = describeStmt(qpos, path) case actionUnknown: qr = &describeUnknownResult{path[0]} default: panic(action) // unreachable } if err != nil { return err } q.Output(lprog.Fset, qr) return nil }
// addCode searches for main func in data, and updates AST code // adding tracing functions. func addCode(path string) ([]byte, error) { var conf loader.Config if _, err := conf.FromArgs([]string{path}, false); err != nil { return nil, err } prog, err := conf.Load() if err != nil { return nil, err } // check if runtime/trace already imported for i, _ := range prog.Imported { if i == "runtime/trace" { return nil, ErrImported } } pkg := prog.Created[0] // TODO: find file with main func inside astFile := pkg.Files[0] // add imports astutil.AddImport(prog.Fset, astFile, "os") astutil.AddImport(prog.Fset, astFile, "runtime/trace") astutil.AddImport(prog.Fset, astFile, "time") // add start/stop code ast.Inspect(astFile, func(n ast.Node) bool { switch x := n.(type) { case *ast.FuncDecl: // find 'main' function if x.Name.Name == "main" && x.Recv == nil { stmts := createTraceStmts() stmts = append(stmts, x.Body.List...) x.Body.List = stmts return true } } return true }) var buf bytes.Buffer err = printer.Fprint(&buf, prog.Fset, astFile) if err != nil { return nil, err } return buf.Bytes(), nil }
func TestMultipleQueries(t *testing.T) { // Loader var buildContext = build.Default buildContext.GOPATH = "testdata" conf := loader.Config{Build: &buildContext} filename := "testdata/src/main/multi.go" conf.CreateFromFilenames("", filename) iprog, err := conf.Load() if err != nil { t.Fatalf("Load failed: %s", err) } // Oracle o, err := oracle.New(iprog, nil, true) if err != nil { t.Fatalf("oracle.New failed: %s", err) } // QueryPos pos := filename + ":#54,#58" qpos, err := oracle.ParseQueryPos(iprog, pos, true) if err != nil { t.Fatalf("oracle.ParseQueryPos(%q) failed: %s", pos, err) } // SSA is built and we have the QueryPos. // Release the other ASTs and type info to the GC. iprog = nil // Run different query modes on same scope and selection. out := new(bytes.Buffer) for _, mode := range [...]string{"callers", "describe", "freevars"} { res, err := o.Query(mode, qpos) if err != nil { t.Errorf("(*oracle.Oracle).Query(%q) failed: %s", pos, err) } WriteResult(out, res) } want := `multi.f is called from these 1 sites: static function call from multi.main function call (or conversion) of type () Free identifiers: var x int ` if got := out.String(); got != want { t.Errorf("Query output differs; want <<%s>>, got <<%s>>\n", want, got) } }
// 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 (p *Parser) Parse(path string) error { dir := filepath.Dir(path) files, err := ioutil.ReadDir(dir) if err != nil { fmt.Printf("// [ERROR] Parse(%s) -> ioutil.ReadDir(%#v) -> err=<%#v>\n", path, dir, err) return err } var astFiles []*ast.File var conf loader.Config conf.TypeCheckFuncBodies = func(_ string) bool { return false } conf.TypeChecker.DisableUnusedImportCheck = true conf.TypeChecker.Importer = importer.Default() for _, fi := range files { if filepath.Ext(fi.Name()) != ".go" { continue } fpath := filepath.Join(dir, fi.Name()) f, err := conf.ParseFile(fpath, nil) if err != nil { fmt.Printf("// [ERROR] Parse(%s) -> conf.ParseFile(%#v) -> err=<%#v>\n", path, fpath, err) return err } if fi.Name() == filepath.Base(path) { p.file = f } astFiles = append(astFiles, f) } abs, err := filepath.Abs(path) if err != nil { fmt.Printf("// [ERROR] Parse(%s) -> filepath.Abs(%#v) -> err=<%#v>\n", path, path, err) return err } // Type-check a package consisting of this file. // Type information for the imported packages // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. conf.CreateFromFiles(abs, astFiles...) prog, err := conf.Load() if err != nil { fmt.Printf("// [ERROR] Parse(%s) -> conf.Load() -> err=<%#v>\n", path, err) return err } else if len(prog.Created) != 1 { panic("expected only one Created package") } p.path = abs p.pkg = prog.Created[0].Pkg return nil }
func TestLoad_FromSource_Success(t *testing.T) { var conf loader.Config conf.CreateFromFilenames("P", "testdata/a.go", "testdata/b.go") 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), "P"; got != want { t.Errorf("Created = %s, want %s", got, want) } }
func TestLoad_NoInitialPackages(t *testing.T) { var conf loader.Config const wantErr = "no initial packages were loaded" 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") } }