go.tools/oracle: support -format=xml (for Eclipse)
This CL is mostly a renaming s/json/serial/, abstracting the oracle package away from any particular data syntax. (The encoding/* machinery is very clean; clearly I should have structured it this way from the outset.) Supporting XML then becomes a one-liner in cmd/oracle/main.go. Also: call MarshalIndent(), not Marshall() then Indent(). R=crawshaw CC=golang-dev https://golang.org/cl/13858046
This commit is contained in:
parent
84cae5a52d
commit
37f76edde8
|
@ -14,8 +14,8 @@ package main
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
|
@ -39,7 +39,7 @@ var modeFlag = flag.String("mode", "",
|
|||
var ptalogFlag = flag.String("ptalog", "",
|
||||
"Location of the points-to analysis log file, or empty to disable logging.")
|
||||
|
||||
var formatFlag = flag.String("format", "plain", "Output format: 'plain' or 'json'.")
|
||||
var formatFlag = flag.String("format", "plain", "Output format. One of {plain,json,xml}.")
|
||||
|
||||
// TODO(adonovan): eliminate or flip this flag after PTA presolver is implemented.
|
||||
var reflectFlag = flag.Bool("reflect", true, "Analyze reflection soundly (slow).")
|
||||
|
@ -53,6 +53,7 @@ The -format flag controls the output format:
|
|||
plain an editor-friendly format in which every line of output
|
||||
is of the form "pos: text", where pos is "-" if unknown.
|
||||
json structured data in JSON syntax.
|
||||
xml structured data in XML syntax.
|
||||
|
||||
The -pos flag is required in all modes except 'callgraph'.
|
||||
|
||||
|
@ -140,7 +141,10 @@ func main() {
|
|||
}
|
||||
|
||||
// -format flag
|
||||
if *formatFlag != "json" && *formatFlag != "plain" {
|
||||
switch *formatFlag {
|
||||
case "json", "plain", "xml":
|
||||
// ok
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Error: illegal -format value: %q\n"+useHelp, *formatFlag)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
@ -161,17 +165,20 @@ func main() {
|
|||
// Print the result.
|
||||
switch *formatFlag {
|
||||
case "json":
|
||||
b, err := json.Marshal(res)
|
||||
b, err := json.MarshalIndent(res.Serial(), "", "\t")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "JSON error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := json.Indent(&buf, b, "", "\t"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "json.Indent failed: %s", err)
|
||||
os.Stdout.Write(b)
|
||||
|
||||
case "xml":
|
||||
b, err := xml.MarshalIndent(res.Serial(), "", "\t")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "XML error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Stdout.Write(buf.Bytes())
|
||||
os.Stdout.Write(b)
|
||||
|
||||
case "plain":
|
||||
res.WriteTo(os.Stdout)
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"sort"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
"code.google.com/p/go.tools/pointer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
@ -113,13 +113,13 @@ func (r *calleesResult) display(printf printfFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *calleesResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
j := &json.Callees{
|
||||
func (r *calleesResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
j := &serial.Callees{
|
||||
Pos: fset.Position(r.site.Pos()).String(),
|
||||
Desc: r.site.Description(),
|
||||
}
|
||||
for _, callee := range r.funcs {
|
||||
j.Callees = append(j.Callees, &json.CalleesItem{
|
||||
j.Callees = append(j.Callees, &serial.CalleesItem{
|
||||
Name: callee.String(),
|
||||
Pos: fset.Position(callee.Pos()).String(),
|
||||
})
|
||||
|
|
|
@ -7,7 +7,7 @@ package oracle
|
|||
import (
|
||||
"go/token"
|
||||
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
"code.google.com/p/go.tools/pointer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
@ -73,10 +73,10 @@ func (r *callersResult) display(printf printfFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *callersResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
var callers []json.Caller
|
||||
func (r *callersResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
var callers []serial.Caller
|
||||
for _, site := range r.calls {
|
||||
var c json.Caller
|
||||
var c serial.Caller
|
||||
c.Caller = site.Caller().Func().String()
|
||||
if site.Caller() == r.root {
|
||||
c.Desc = "synthetic call"
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"go/token"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
"code.google.com/p/go.tools/pointer"
|
||||
)
|
||||
|
||||
|
@ -90,8 +90,8 @@ Some nodes may appear multiple times due to context-sensitive
|
|||
print(r.root, 0)
|
||||
}
|
||||
|
||||
func (r *callgraphResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
cg := make([]json.CallGraph, len(r.numbering))
|
||||
func (r *callgraphResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
cg := make([]serial.CallGraph, len(r.numbering))
|
||||
for n, i := range r.numbering {
|
||||
j := &cg[i]
|
||||
fn := n.Func()
|
||||
|
|
|
@ -7,7 +7,7 @@ package oracle
|
|||
import (
|
||||
"go/token"
|
||||
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
"code.google.com/p/go.tools/pointer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
@ -98,16 +98,16 @@ func (r *callstackResult) display(printf printfFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *callstackResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
var callers []json.Caller
|
||||
func (r *callstackResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
var callers []serial.Caller
|
||||
for _, site := range r.callstack {
|
||||
callers = append(callers, json.Caller{
|
||||
callers = append(callers, serial.Caller{
|
||||
Pos: fset.Position(site.Pos()).String(),
|
||||
Caller: site.Caller().Func().String(),
|
||||
Desc: site.Description(),
|
||||
})
|
||||
}
|
||||
res.Callstack = &json.CallStack{
|
||||
res.Callstack = &serial.CallStack{
|
||||
Pos: fset.Position(r.target.Pos()).String(),
|
||||
Target: r.target.String(),
|
||||
Callers: callers,
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/importer"
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
"code.google.com/p/go.tools/pointer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
@ -70,8 +70,8 @@ func (r *describeUnknownResult) display(printf printfFunc) {
|
|||
printf(r.node, "%s", importer.NodeDescription(r.node))
|
||||
}
|
||||
|
||||
func (r *describeUnknownResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
res.Describe = &json.Describe{
|
||||
func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
res.Describe = &serial.Describe{
|
||||
Desc: importer.NodeDescription(r.node),
|
||||
Pos: fset.Position(r.node.Pos()).String(),
|
||||
}
|
||||
|
@ -553,7 +553,7 @@ func (r *describeValueResult) display(printf printfFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *describeValueResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
var value, objpos, ptaerr string
|
||||
if r.constVal != nil {
|
||||
value = r.constVal.String()
|
||||
|
@ -565,31 +565,31 @@ func (r *describeValueResult) toJSON(res *json.Result, fset *token.FileSet) {
|
|||
ptaerr = r.ptaErr.Error()
|
||||
}
|
||||
|
||||
var pts []*json.DescribePointer
|
||||
var pts []*serial.DescribePointer
|
||||
for _, ptr := range r.ptrs {
|
||||
var namePos string
|
||||
if nt, ok := deref(ptr.typ).(*types.Named); ok {
|
||||
namePos = fset.Position(nt.Obj().Pos()).String()
|
||||
}
|
||||
var labels []json.DescribePTALabel
|
||||
var labels []serial.DescribePTALabel
|
||||
for _, l := range ptr.labels {
|
||||
labels = append(labels, json.DescribePTALabel{
|
||||
labels = append(labels, serial.DescribePTALabel{
|
||||
Pos: fset.Position(l.Pos()).String(),
|
||||
Desc: l.String(),
|
||||
})
|
||||
}
|
||||
pts = append(pts, &json.DescribePointer{
|
||||
pts = append(pts, &serial.DescribePointer{
|
||||
Type: ptr.typ.String(),
|
||||
NamePos: namePos,
|
||||
Labels: labels,
|
||||
})
|
||||
}
|
||||
|
||||
res.Describe = &json.Describe{
|
||||
res.Describe = &serial.Describe{
|
||||
Desc: importer.NodeDescription(r.expr),
|
||||
Pos: fset.Position(r.expr.Pos()).String(),
|
||||
Detail: "value",
|
||||
Value: &json.DescribeValue{
|
||||
Value: &serial.DescribeValue{
|
||||
Type: r.typ.String(),
|
||||
Value: value,
|
||||
ObjPos: objpos,
|
||||
|
@ -689,21 +689,21 @@ func (r *describeTypeResult) display(printf printfFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *describeTypeResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
var namePos, nameDef string
|
||||
if nt, ok := r.typ.(*types.Named); ok {
|
||||
namePos = fset.Position(nt.Obj().Pos()).String()
|
||||
nameDef = nt.Underlying().String()
|
||||
}
|
||||
res.Describe = &json.Describe{
|
||||
res.Describe = &serial.Describe{
|
||||
Desc: r.description,
|
||||
Pos: fset.Position(r.node.Pos()).String(),
|
||||
Detail: "type",
|
||||
Type: &json.DescribeType{
|
||||
Type: &serial.DescribeType{
|
||||
Type: r.typ.String(),
|
||||
NamePos: namePos,
|
||||
NameDef: nameDef,
|
||||
Methods: methodsToJSON(r.methods, fset),
|
||||
Methods: methodsToSerial(r.methods, fset),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -831,8 +831,8 @@ func formatMember(obj types.Object, maxname int) string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
func (r *describePackageResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
var members []*json.DescribeMember
|
||||
func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
var members []*serial.DescribeMember
|
||||
for _, mem := range r.members {
|
||||
typ := mem.obj.Type()
|
||||
var val string
|
||||
|
@ -842,20 +842,20 @@ func (r *describePackageResult) toJSON(res *json.Result, fset *token.FileSet) {
|
|||
case *types.TypeName:
|
||||
typ = typ.Underlying()
|
||||
}
|
||||
members = append(members, &json.DescribeMember{
|
||||
members = append(members, &serial.DescribeMember{
|
||||
Name: mem.obj.Name(),
|
||||
Type: typ.String(),
|
||||
Value: val,
|
||||
Pos: fset.Position(mem.obj.Pos()).String(),
|
||||
Kind: tokenOf(mem.obj),
|
||||
Methods: methodsToJSON(mem.methods, fset),
|
||||
Methods: methodsToSerial(mem.methods, fset),
|
||||
})
|
||||
}
|
||||
res.Describe = &json.Describe{
|
||||
res.Describe = &serial.Describe{
|
||||
Desc: r.description,
|
||||
Pos: fset.Position(r.node.Pos()).String(),
|
||||
Detail: "package",
|
||||
Package: &json.DescribePackage{
|
||||
Package: &serial.DescribePackage{
|
||||
Path: r.pkg.Path(),
|
||||
Members: members,
|
||||
},
|
||||
|
@ -907,8 +907,8 @@ func (r *describeStmtResult) display(printf printfFunc) {
|
|||
printf(r.node, "%s", r.description)
|
||||
}
|
||||
|
||||
func (r *describeStmtResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
res.Describe = &json.Describe{
|
||||
func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
res.Describe = &serial.Describe{
|
||||
Desc: r.description,
|
||||
Pos: fset.Position(r.node.Pos()).String(),
|
||||
Detail: "unknown",
|
||||
|
@ -946,10 +946,10 @@ func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
|
|||
return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
|
||||
}
|
||||
|
||||
func methodsToJSON(methods []*types.Selection, fset *token.FileSet) []json.DescribeMethod {
|
||||
var jmethods []json.DescribeMethod
|
||||
func methodsToSerial(methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod {
|
||||
var jmethods []serial.DescribeMethod
|
||||
for _, meth := range methods {
|
||||
jmethods = append(jmethods, json.DescribeMethod{
|
||||
jmethods = append(jmethods, serial.DescribeMethod{
|
||||
Name: meth.String(),
|
||||
Pos: fset.Position(meth.Obj().Pos()).String(),
|
||||
})
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"sort"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
)
|
||||
|
||||
// freevars displays the lexical (not package-level) free variables of
|
||||
|
@ -169,11 +169,11 @@ func (r *freevarsResult) display(printf printfFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *freevarsResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
var refs []*json.FreeVar
|
||||
func (r *freevarsResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
var refs []*serial.FreeVar
|
||||
for _, ref := range r.refs {
|
||||
refs = append(refs,
|
||||
&json.FreeVar{
|
||||
&serial.FreeVar{
|
||||
Pos: fset.Position(ref.obj.Pos()).String(),
|
||||
Kind: ref.kind,
|
||||
Ref: ref.ref,
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"go/token"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
)
|
||||
|
||||
// Implements displays the 'implements" relation among all
|
||||
|
@ -89,10 +89,10 @@ func (r *implementsResult) display(printf printfFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *implementsResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
var facts []*json.Implements
|
||||
func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
var facts []*serial.Implements
|
||||
for _, fact := range r.facts {
|
||||
facts = append(facts, &json.Implements{
|
||||
facts = append(facts, &serial.Implements{
|
||||
I: fact.iface.String(),
|
||||
IPos: fset.Position(fact.iface.Obj().Pos()).String(),
|
||||
C: fact.conc.String(),
|
||||
|
|
|
@ -18,7 +18,6 @@ package oracle
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
encjson "encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
@ -34,7 +33,7 @@ import (
|
|||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/importer"
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
"code.google.com/p/go.tools/pointer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
@ -97,7 +96,7 @@ type printfFunc func(pos interface{}, format string, args ...interface{})
|
|||
|
||||
// queryResult is the interface of each query-specific result type.
|
||||
type queryResult interface {
|
||||
toJSON(res *json.Result, fset *token.FileSet)
|
||||
toSerial(res *serial.Result, fset *token.FileSet)
|
||||
display(printf printfFunc)
|
||||
}
|
||||
|
||||
|
@ -119,9 +118,6 @@ type QueryPos struct {
|
|||
}
|
||||
|
||||
// A Result encapsulates the result of an oracle.Query.
|
||||
//
|
||||
// Result instances implement the json.Marshaler interface, i.e. they
|
||||
// can be JSON-serialized.
|
||||
type Result struct {
|
||||
fset *token.FileSet
|
||||
// fprintf is a closure over the oracle's fileset and start/end position.
|
||||
|
@ -131,16 +127,20 @@ type Result struct {
|
|||
warnings []warning // pointer analysis warnings
|
||||
}
|
||||
|
||||
func (res *Result) MarshalJSON() ([]byte, error) {
|
||||
resj := &json.Result{Mode: res.mode}
|
||||
res.q.toJSON(resj, res.fset)
|
||||
// Serial returns an instance of serial.Result, which implements the
|
||||
// {xml,json}.Marshaler interfaces so that query results can be
|
||||
// serialized as JSON or XML.
|
||||
//
|
||||
func (res *Result) Serial() *serial.Result {
|
||||
resj := &serial.Result{Mode: res.mode}
|
||||
res.q.toSerial(resj, res.fset)
|
||||
for _, w := range res.warnings {
|
||||
resj.Warnings = append(resj.Warnings, json.PTAWarning{
|
||||
resj.Warnings = append(resj.Warnings, serial.PTAWarning{
|
||||
Pos: res.fset.Position(w.pos).String(),
|
||||
Message: fmt.Sprintf(w.format, w.args...),
|
||||
})
|
||||
}
|
||||
return encjson.Marshal(resj)
|
||||
return resj
|
||||
}
|
||||
|
||||
// Query runs a single oracle query.
|
||||
|
|
|
@ -175,17 +175,12 @@ func doQuery(out io.Writer, q *query, useJson bool) {
|
|||
|
||||
if useJson {
|
||||
// JSON output
|
||||
b, err := json.Marshal(res)
|
||||
b, err := json.MarshalIndent(res.Serial(), "", "\t")
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "JSON error: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := json.Indent(&buf, b, "", "\t"); err != nil {
|
||||
fmt.Fprintf(out, "json.Indent failed: %s", err)
|
||||
return
|
||||
}
|
||||
out.Write(buf.Bytes())
|
||||
out.Write(b)
|
||||
} else {
|
||||
// "plain" (compiler diagnostic format) output
|
||||
capture := new(bytes.Buffer) // capture standard output
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"sort"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
"code.google.com/p/go.tools/pointer"
|
||||
"code.google.com/p/go.tools/ssa"
|
||||
)
|
||||
|
@ -174,8 +174,8 @@ func (r *peersResult) display(printf printfFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *peersResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
peers := &json.Peers{
|
||||
func (r *peersResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
peers := &serial.Peers{
|
||||
Pos: fset.Position(r.queryPos).String(),
|
||||
Type: r.queryType.String(),
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"sort"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"code.google.com/p/go.tools/oracle/json"
|
||||
"code.google.com/p/go.tools/oracle/serial"
|
||||
)
|
||||
|
||||
// Referrers reports all identifiers that resolve to the same object
|
||||
|
@ -84,8 +84,8 @@ func (r *referrersResult) display(printf printfFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *referrersResult) toJSON(res *json.Result, fset *token.FileSet) {
|
||||
referrers := &json.Referrers{
|
||||
func (r *referrersResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
||||
referrers := &serial.Referrers{
|
||||
Pos: fset.Position(r.query).String(),
|
||||
Desc: r.obj.String(),
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package json defines the oracle's JSON schema.
|
||||
package json
|
||||
// Package serial defines the oracle's schema for structured data
|
||||
// serialization using JSON, XML, etc.
|
||||
package serial
|
||||
|
||||
// All 'pos' strings are of the form "file:line:col".
|
||||
// TODO(adonovan): improve performance by sharing filename strings.
|
Loading…
Reference in New Issue