// Oracle annotates `pkg` using go.tools/oracle interface implements detector. // It uses `scopes` as analysis scope. // If `scopes` is none of one of `scopes` is zero string, it uses unit tests as scope. func Oracle(pkg *ast.Package, scopes ...string) error { settings := build.Default settings.BuildTags = []string{} // TODO conf := loader.Config{Build: &settings, SourceImports: true} withTests := false if len(scopes) == 0 { withTests = true } for _, scope := range scopes { if scope == "" { withTests = true } else { conf.Import(scope) } } if withTests { conf.ImportWithTests(pkg.Name) } else { conf.Import(pkg.Name) } iprog, err := conf.Load() if err != nil { return fmt.Errorf("oracle annotator: conf load error: %+v", err) } o, err := oracle.New(iprog, nil, false) if err != nil { return fmt.Errorf("oracle annotator: create error: %+v", err) } for _, class := range pkg.Classes { qpos, err := oracle.ParseQueryPos(iprog, string(class.Pos), false) if err != nil { log.Printf("oracle annotator: parse query pos error: %+v, %+v", err, class.Pos) continue } res, err := o.Query("implements", qpos) if err != nil { return fmt.Errorf("oracle annotator: query error: %+v, %v", err, class.Pos) } impls := res.Serial().Implements for _, target := range impls.AssignableFromPtr { addImplements(class, target) } for _, target := range impls.AssignableFrom { addImplements(class, target) } } return nil }
func TestMultipleQueries(t *testing.T) { // Loader var buildContext = build.Default buildContext.GOPATH = "testdata" conf := loader.Config{Build: &buildContext, SourceImports: true} 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) } }
func TestMultipleQueries(t *testing.T) { // Importer var buildContext = build.Default buildContext.GOPATH = "testdata" imp := importer.New(&importer.Config{Build: &buildContext}) // Oracle filename := "testdata/src/main/multi.go" o, err := oracle.New(imp, []string{filename}, nil, true) if err != nil { t.Fatalf("oracle.New failed: %s", err) } // QueryPos pos := filename + ":#54,#58" qpos, err := oracle.ParseQueryPos(imp, 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. imp = nil // Run different query moes 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) } capture := new(bytes.Buffer) // capture standard output res.WriteTo(capture) for _, line := range strings.Split(capture.String(), "\n") { fmt.Fprintf(out, "%s\n", stripLocation(line)) } } 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) } }
func main() { flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) } flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) if err := flag.CommandLine.Parse(os.Args[1:]); err != nil { if err == flag.ErrHelp { fmt.Println(helpMessage) fmt.Println("Flags:") flag.PrintDefaults() } os.Exit(2) } args = flag.Args() if len(args) == 0 { fmt.Fprint(os.Stderr, "Error: no package arguments.\n"+useHelp) os.Exit(2) } var err error imp = importer.New(&importer.Config{Build: &build.Default}) ora, err = oracle.New(imp, args, nil, false) if err != nil { log.Fatal(err) } files = scopeFiles(imp) packages = imp.AllPackages() sort.Sort(byPath(packages)) registerHandlers() srv := &http.Server{Addr: *httpAddr} l, err := net.Listen("tcp", srv.Addr) if err != nil { log.Fatal(err) } if *open { url := fmt.Sprintf("http://localhost%s/", *httpAddr) if !startBrowser(url) { fmt.Println(url) } } log.Fatal(srv.Serve(l)) }
func main() { flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage: errorpaths scope pkg-pattern\n") fmt.Fprint(os.Stderr, loader.FromArgsUsage) } flag.Parse() args := flag.Args() if len(args) != 2 { flag.Usage() } conf := loader.Config{ SourceImports: true, } _, err := conf.FromArgs(args[0:1]) if err != nil { log.Fatalf("cannot initialise loader: %v", err) } pkgPat, err := regexp.Compile("^" + args[1] + "$") if err != nil { log.Fatalf("cann compile regexp %q: %s", args[1], err) } lprog, err := conf.Load() if err != nil { log.Fatalf("cannot load program: %v", err) } or, err := oracle.New(lprog, nil, false) if err != nil { log.Fatalf("cannot make oracle: %v", err) } ssaProg := ssa.Create(lprog, ssa.SanityCheckFunctions) ctxt := &context{ lprog: lprog, ssaProg: ssaProg, oracle: or, infos: make(map[*ssa.Function]*errorInfo), locs: make(map[*ssa.Function]errorLocations), } var foundPkgs []*types.Package log.Printf("searching %d packages", len(lprog.AllPackages)) for pkg, _ := range lprog.AllPackages { if pkgPat.MatchString(pkg.Path()) { foundPkgs = append(foundPkgs, pkg) break } } if len(foundPkgs) == 0 { log.Fatalf("failed to find any matching packages") } for _, pkg := range foundPkgs { log.Printf("package %s", pkg.Name()) ssaPkg := ssaProg.Package(pkg) ssaPkg.Build() for name, m := range ssaPkg.Members { log.Printf("name %s", name) if f, ok := m.(*ssa.Function); ok && returnsError(f) { fmt.Printf("%s\n", f) locs := ctxt.errorLocations(f) ctxt.dumpErrorLocs(locs, os.Stdout, "\t") } } } }