示例#1
0
// 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
}
示例#2
0
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)
	}
}
示例#3
0
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)
	}
}
示例#4
0
func (ctxt *context) callees(inst *ssa.Call) ([]*ssa.Function, error) {
	pos := ctxt.lprog.Fset.Position(inst.Pos())
	if pos.Line <= 0 {
		return nil, fmt.Errorf("no position")
	}
	qpos, err := oracle.ParseQueryPos(ctxt.lprog, posStr(pos), true)
	if err != nil {
		return nil, fmt.Errorf("cannot parse query pos %q: %v", posStr(pos), err)
	}
	result, err := ctxt.oracle.Query("callees", qpos)
	if err != nil {
		return nil, fmt.Errorf("query error: %v", err)
	}
	return calleeFuncs(result), nil
}
示例#5
0
// serveQuery executes a query to the oracle and delivers the results
// in the specified format. The request parameters are:
//
//   mode: e.g. "describe", "callers", "freevars", ...
//   pos: file name with byte offset(s), e.g. "/path/to/file.go:#1457,#1462"
//   format: "json" or "plain", no "xml" at the moment
//
// If the application was launched in verbose mode, each query will be
// logged like an invocation of the oracle command.
func serveQuery(w http.ResponseWriter, req *http.Request) {
	mode := req.FormValue("mode")
	pos := req.FormValue("pos")
	format := req.FormValue("format")
	if *verbose {
		log.Println(req.RemoteAddr, cmdLine(mode, pos, format, args))
	}
	qpos, err := oracle.ParseQueryPos(imp, pos, false)
	if err != nil {
		io.WriteString(w, err.Error())
		return
	}
	res, err := queryOracle(mode, qpos)
	if err != nil {
		io.WriteString(w, err.Error())
		return
	}
	writeResult(w, res, format)
}