2013-08-27 16:49:13 -06:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
package oracle
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
2013-09-03 13:29:02 -06:00
|
|
|
"os"
|
2013-08-27 15:58:26 -06:00
|
|
|
"strings"
|
|
|
|
|
2013-12-09 07:36:29 -07:00
|
|
|
"code.google.com/p/go.tools/astutil"
|
2013-09-03 13:29:02 -06:00
|
|
|
"code.google.com/p/go.tools/go/exact"
|
2014-01-16 07:33:58 -07:00
|
|
|
"code.google.com/p/go.tools/go/loader"
|
|
|
|
"code.google.com/p/go.tools/go/ssa"
|
2013-08-27 15:58:26 -06:00
|
|
|
"code.google.com/p/go.tools/go/types"
|
2013-09-24 13:08:14 -06:00
|
|
|
"code.google.com/p/go.tools/oracle/serial"
|
2013-08-27 15:58:26 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// describe describes the syntax node denoted by the query position,
|
|
|
|
// including:
|
|
|
|
// - its syntactic category
|
2013-12-13 08:04:55 -07:00
|
|
|
// - the definition of its referent (for identifiers) [now redundant]
|
2013-08-27 15:58:26 -06:00
|
|
|
// - its type and method set (for an expression or type expression)
|
2013-09-03 13:29:02 -06:00
|
|
|
//
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
func describe(o *Oracle, qpos *QueryPos) (queryResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
if false { // debugging
|
2013-12-13 08:04:55 -07:00
|
|
|
fprintf(os.Stderr, o.fset, qpos.path[0], "you selected: %s %s",
|
|
|
|
astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path))
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
path, action := findInterestingNode(qpos.info, qpos.path)
|
2013-08-27 15:58:26 -06:00
|
|
|
switch action {
|
|
|
|
case actionExpr:
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
return describeValue(o, qpos, path)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case actionType:
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
return describeType(o, qpos, path)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case actionPackage:
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
return describePackage(o, qpos, path)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case actionStmt:
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
return describeStmt(o, qpos, path)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case actionUnknown:
|
|
|
|
return &describeUnknownResult{path[0]}, nil
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic(action) // unreachable
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type describeUnknownResult struct {
|
|
|
|
node ast.Node
|
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describeUnknownResult) display(printf printfFunc) {
|
2013-08-27 15:58:26 -06:00
|
|
|
// Nothing much to say about misc syntax.
|
2013-12-09 07:36:29 -07:00
|
|
|
printf(r.node, "%s", astutil.NodeDescription(r.node))
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
|
|
|
res.Describe = &serial.Describe{
|
2013-12-09 07:36:29 -07:00
|
|
|
Desc: astutil.NodeDescription(r.node),
|
2013-09-03 13:29:02 -06:00
|
|
|
Pos: fset.Position(r.node.Pos()).String(),
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type action int
|
|
|
|
|
|
|
|
const (
|
|
|
|
actionUnknown action = iota // None of the below
|
|
|
|
actionExpr // FuncDecl, true Expr or Ident(types.{Const,Var})
|
|
|
|
actionType // type Expr or Ident(types.TypeName).
|
|
|
|
actionStmt // Stmt or Ident(types.Label)
|
|
|
|
actionPackage // Ident(types.Package) or ImportSpec
|
|
|
|
)
|
|
|
|
|
|
|
|
// findInterestingNode classifies the syntax node denoted by path as one of:
|
|
|
|
// - an expression, part of an expression or a reference to a constant
|
|
|
|
// or variable;
|
|
|
|
// - a type, part of a type, or a reference to a named type;
|
|
|
|
// - a statement, part of a statement, or a label referring to a statement;
|
|
|
|
// - part of a package declaration or import spec.
|
|
|
|
// - none of the above.
|
|
|
|
// and returns the most "interesting" associated node, which may be
|
|
|
|
// the same node, an ancestor or a descendent.
|
|
|
|
//
|
2014-01-16 07:33:58 -07:00
|
|
|
func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) {
|
2013-08-27 15:58:26 -06:00
|
|
|
// TODO(adonovan): integrate with go/types/stdlib_test.go and
|
|
|
|
// apply this to every AST node we can find to make sure it
|
|
|
|
// doesn't crash.
|
|
|
|
|
|
|
|
// TODO(adonovan): audit for ParenExpr safety, esp. since we
|
|
|
|
// traverse up and down.
|
|
|
|
|
|
|
|
// TODO(adonovan): if the users selects the "." in
|
|
|
|
// "fmt.Fprintf()", they'll get an ambiguous selection error;
|
|
|
|
// we won't even reach here. Can we do better?
|
|
|
|
|
|
|
|
// TODO(adonovan): describing a field within 'type T struct {...}'
|
2013-09-03 13:29:02 -06:00
|
|
|
// describes the (anonymous) struct type and concludes "no methods".
|
|
|
|
// We should ascend to the enclosing type decl, if any.
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
for len(path) > 0 {
|
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.GenDecl:
|
|
|
|
if len(n.Specs) == 1 {
|
|
|
|
// Descend to sole {Import,Type,Value}Spec child.
|
|
|
|
path = append([]ast.Node{n.Specs[0]}, path...)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return path, actionUnknown // uninteresting
|
|
|
|
|
|
|
|
case *ast.FuncDecl:
|
|
|
|
// Descend to function name.
|
|
|
|
path = append([]ast.Node{n.Name}, path...)
|
|
|
|
continue
|
|
|
|
|
|
|
|
case *ast.ImportSpec:
|
|
|
|
return path, actionPackage
|
|
|
|
|
|
|
|
case *ast.ValueSpec:
|
|
|
|
if len(n.Names) == 1 {
|
|
|
|
// Descend to sole Ident child.
|
|
|
|
path = append([]ast.Node{n.Names[0]}, path...)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return path, actionUnknown // uninteresting
|
|
|
|
|
|
|
|
case *ast.TypeSpec:
|
|
|
|
// Descend to type name.
|
|
|
|
path = append([]ast.Node{n.Name}, path...)
|
|
|
|
continue
|
|
|
|
|
|
|
|
case ast.Stmt:
|
|
|
|
return path, actionStmt
|
|
|
|
|
|
|
|
case *ast.ArrayType,
|
|
|
|
*ast.StructType,
|
|
|
|
*ast.FuncType,
|
|
|
|
*ast.InterfaceType,
|
|
|
|
*ast.MapType,
|
|
|
|
*ast.ChanType:
|
|
|
|
return path, actionType
|
|
|
|
|
|
|
|
case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause:
|
|
|
|
return path, actionUnknown // uninteresting
|
|
|
|
|
|
|
|
case *ast.Ellipsis:
|
|
|
|
// Continue to enclosing node.
|
|
|
|
// e.g. [...]T in ArrayType
|
|
|
|
// f(x...) in CallExpr
|
|
|
|
// f(x...T) in FuncType
|
|
|
|
|
|
|
|
case *ast.Field:
|
|
|
|
// TODO(adonovan): this needs more thought,
|
|
|
|
// since fields can be so many things.
|
|
|
|
if len(n.Names) == 1 {
|
|
|
|
// Descend to sole Ident child.
|
|
|
|
path = append([]ast.Node{n.Names[0]}, path...)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Zero names (e.g. anon field in struct)
|
|
|
|
// or multiple field or param names:
|
|
|
|
// continue to enclosing field list.
|
|
|
|
|
|
|
|
case *ast.FieldList:
|
|
|
|
// Continue to enclosing node:
|
|
|
|
// {Struct,Func,Interface}Type or FuncDecl.
|
|
|
|
|
|
|
|
case *ast.BasicLit:
|
|
|
|
if _, ok := path[1].(*ast.ImportSpec); ok {
|
|
|
|
return path[1:], actionPackage
|
|
|
|
}
|
|
|
|
return path, actionExpr
|
|
|
|
|
|
|
|
case *ast.SelectorExpr:
|
|
|
|
if pkginfo.ObjectOf(n.Sel) == nil {
|
2013-12-13 08:04:55 -07:00
|
|
|
// TODO(adonovan): is this reachable?
|
2013-08-27 15:58:26 -06:00
|
|
|
return path, actionUnknown
|
|
|
|
}
|
|
|
|
// Descend to .Sel child.
|
|
|
|
path = append([]ast.Node{n.Sel}, path...)
|
|
|
|
continue
|
|
|
|
|
|
|
|
case *ast.Ident:
|
2013-11-14 15:11:43 -07:00
|
|
|
switch pkginfo.ObjectOf(n).(type) {
|
2013-09-13 10:52:57 -06:00
|
|
|
case *types.PkgName:
|
2013-08-27 15:58:26 -06:00
|
|
|
return path, actionPackage
|
|
|
|
|
|
|
|
case *types.Const:
|
|
|
|
return path, actionExpr
|
|
|
|
|
|
|
|
case *types.Label:
|
|
|
|
return path, actionStmt
|
|
|
|
|
|
|
|
case *types.TypeName:
|
|
|
|
return path, actionType
|
|
|
|
|
|
|
|
case *types.Var:
|
|
|
|
// For x in 'struct {x T}', return struct type, for now.
|
|
|
|
if _, ok := path[1].(*ast.Field); ok {
|
|
|
|
_ = path[2].(*ast.FieldList) // assertion
|
|
|
|
if _, ok := path[3].(*ast.StructType); ok {
|
|
|
|
return path[3:], actionType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return path, actionExpr
|
|
|
|
|
|
|
|
case *types.Func:
|
|
|
|
// For f in 'interface {f()}', return the interface type, for now.
|
|
|
|
if _, ok := path[1].(*ast.Field); ok {
|
|
|
|
_ = path[2].(*ast.FieldList) // assertion
|
|
|
|
if _, ok := path[3].(*ast.InterfaceType); ok {
|
|
|
|
return path[3:], actionType
|
|
|
|
}
|
|
|
|
}
|
2013-11-14 15:11:43 -07:00
|
|
|
return path, actionExpr
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-11-14 15:11:43 -07:00
|
|
|
case *types.Builtin:
|
2013-08-27 15:58:26 -06:00
|
|
|
// For reference to built-in function, return enclosing call.
|
2013-11-14 15:11:43 -07:00
|
|
|
path = path[1:] // ascend to enclosing function call
|
|
|
|
continue
|
2013-12-13 08:04:55 -07:00
|
|
|
|
|
|
|
case *types.Nil:
|
|
|
|
return path, actionExpr
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// No object.
|
|
|
|
switch path[1].(type) {
|
|
|
|
case *ast.SelectorExpr:
|
|
|
|
// Return enclosing selector expression.
|
|
|
|
return path[1:], actionExpr
|
|
|
|
|
|
|
|
case *ast.Field:
|
|
|
|
// TODO(adonovan): test this.
|
|
|
|
// e.g. all f in:
|
|
|
|
// struct { f, g int }
|
|
|
|
// interface { f() }
|
|
|
|
// func (f T) method(f, g int) (f, g bool)
|
|
|
|
//
|
|
|
|
// switch path[3].(type) {
|
|
|
|
// case *ast.FuncDecl:
|
|
|
|
// case *ast.StructType:
|
|
|
|
// case *ast.InterfaceType:
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// return path[1:], actionExpr
|
|
|
|
//
|
|
|
|
// Unclear what to do with these.
|
|
|
|
// Struct.Fields -- field
|
|
|
|
// Interface.Methods -- field
|
|
|
|
// FuncType.{Params.Results} -- actionExpr
|
|
|
|
// FuncDecl.Recv -- actionExpr
|
|
|
|
|
2013-09-13 10:52:57 -06:00
|
|
|
case *ast.File:
|
|
|
|
// 'package foo'
|
|
|
|
return path, actionPackage
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
case *ast.ImportSpec:
|
|
|
|
// TODO(adonovan): fix: why no package object? go/types bug?
|
|
|
|
return path[1:], actionPackage
|
|
|
|
|
|
|
|
default:
|
|
|
|
// e.g. blank identifier (go/types bug?)
|
|
|
|
// or y in "switch y := x.(type)" (go/types bug?)
|
2013-12-13 08:04:55 -07:00
|
|
|
// or code in a _test.go file that's not part of the package.
|
2013-08-27 15:58:26 -06:00
|
|
|
fmt.Printf("unknown reference %s in %T\n", n, path[1])
|
|
|
|
return path, actionUnknown
|
|
|
|
}
|
|
|
|
|
|
|
|
case *ast.StarExpr:
|
|
|
|
if pkginfo.IsType(n) {
|
|
|
|
return path, actionType
|
|
|
|
}
|
|
|
|
return path, actionExpr
|
|
|
|
|
|
|
|
case ast.Expr:
|
|
|
|
// All Expr but {BasicLit,Ident,StarExpr} are
|
|
|
|
// "true" expressions that evaluate to a value.
|
|
|
|
return path, actionExpr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ascend to parent.
|
|
|
|
path = path[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, actionUnknown // unreachable
|
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
func describeValue(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeValueResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
var expr ast.Expr
|
2013-09-09 19:06:25 -06:00
|
|
|
var obj types.Object
|
2013-08-27 15:58:26 -06:00
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.ValueSpec:
|
|
|
|
// ambiguous ValueSpec containing multiple names
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, fmt.Errorf("multiple value specification")
|
2013-09-09 19:06:25 -06:00
|
|
|
case *ast.Ident:
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
obj = qpos.info.ObjectOf(n)
|
2013-09-09 19:06:25 -06:00
|
|
|
expr = n
|
2013-08-27 15:58:26 -06:00
|
|
|
case ast.Expr:
|
|
|
|
expr = n
|
|
|
|
default:
|
2013-12-13 08:04:55 -07:00
|
|
|
// TODO(adonovan): is this reachable?
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, fmt.Errorf("unexpected AST for expr: %T", n)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
typ := qpos.info.TypeOf(expr)
|
|
|
|
constVal := qpos.info.ValueOf(expr)
|
2013-09-09 19:06:25 -06:00
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
return &describeValueResult{
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
qpos: qpos,
|
2013-09-03 13:29:02 -06:00
|
|
|
expr: expr,
|
|
|
|
typ: typ,
|
|
|
|
constVal: constVal,
|
|
|
|
obj: obj,
|
2013-08-27 15:58:26 -06:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type describeValueResult struct {
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
qpos *QueryPos
|
2013-12-13 08:04:55 -07:00
|
|
|
expr ast.Expr // query node
|
|
|
|
typ types.Type // type of expression
|
|
|
|
constVal exact.Value // value of expression, if constant
|
|
|
|
obj types.Object // var/func/const object, if expr was Ident
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describeValueResult) display(printf printfFunc) {
|
2013-09-04 14:15:41 -06:00
|
|
|
var prefix, suffix string
|
2013-09-03 13:29:02 -06:00
|
|
|
if r.constVal != nil {
|
|
|
|
suffix = fmt.Sprintf(" of constant value %s", r.constVal)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-04 14:15:41 -06:00
|
|
|
switch obj := r.obj.(type) {
|
|
|
|
case *types.Func:
|
|
|
|
if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
|
|
|
|
if _, ok := recv.Type().Underlying().(*types.Interface); ok {
|
|
|
|
prefix = "interface method "
|
|
|
|
} else {
|
|
|
|
prefix = "method "
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
// Describe the expression.
|
|
|
|
if r.obj != nil {
|
|
|
|
if r.obj.Pos() == r.expr.Pos() {
|
|
|
|
// defining ident
|
2013-11-15 07:22:16 -07:00
|
|
|
printf(r.expr, "definition of %s%s%s", prefix, r.qpos.ObjectString(r.obj), suffix)
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
|
|
|
// referring ident
|
2013-11-15 07:22:16 -07:00
|
|
|
printf(r.expr, "reference to %s%s%s", prefix, r.qpos.ObjectString(r.obj), suffix)
|
2013-08-27 15:58:26 -06:00
|
|
|
if def := r.obj.Pos(); def != token.NoPos {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(def, "defined here")
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2013-12-09 07:36:29 -07:00
|
|
|
desc := astutil.NodeDescription(r.expr)
|
2013-08-27 15:58:26 -06:00
|
|
|
if suffix != "" {
|
|
|
|
// constant expression
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(r.expr, "%s%s", desc, suffix)
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
|
|
|
// non-constant expression
|
2013-11-15 07:22:16 -07:00
|
|
|
printf(r.expr, "%s of type %s", desc, r.qpos.TypeString(r.typ))
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
2013-12-13 08:04:55 -07:00
|
|
|
var value, objpos string
|
2013-09-03 13:29:02 -06:00
|
|
|
if r.constVal != nil {
|
|
|
|
value = r.constVal.String()
|
|
|
|
}
|
|
|
|
if r.obj != nil {
|
|
|
|
objpos = fset.Position(r.obj.Pos()).String()
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
res.Describe = &serial.Describe{
|
2013-12-09 07:36:29 -07:00
|
|
|
Desc: astutil.NodeDescription(r.expr),
|
2013-09-03 13:29:02 -06:00
|
|
|
Pos: fset.Position(r.expr.Pos()).String(),
|
|
|
|
Detail: "value",
|
2013-09-24 13:08:14 -06:00
|
|
|
Value: &serial.DescribeValue{
|
2013-11-15 07:22:16 -07:00
|
|
|
Type: r.qpos.TypeString(r.typ),
|
2013-09-03 13:29:02 -06:00
|
|
|
Value: value,
|
|
|
|
ObjPos: objpos,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
// ---- TYPE ------------------------------------------------------------
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
func describeType(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeTypeResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
var description string
|
|
|
|
var t types.Type
|
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.Ident:
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
t = qpos.info.TypeOf(n)
|
2013-08-27 15:58:26 -06:00
|
|
|
switch t := t.(type) {
|
|
|
|
case *types.Basic:
|
2013-11-15 07:22:16 -07:00
|
|
|
description = "reference to built-in "
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case *types.Named:
|
|
|
|
isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above
|
|
|
|
if isDef {
|
2013-11-15 07:22:16 -07:00
|
|
|
description = "definition of "
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
2013-11-15 07:22:16 -07:00
|
|
|
description = "reference to "
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case ast.Expr:
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
t = qpos.info.TypeOf(n)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
default:
|
|
|
|
// Unreachable?
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, fmt.Errorf("unexpected AST for type: %T", n)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-11-20 14:00:23 -07:00
|
|
|
description = description + "type " + qpos.TypeString(t)
|
|
|
|
|
|
|
|
// Show sizes for structs and named types (it's fairly obvious for others).
|
|
|
|
switch t.(type) {
|
|
|
|
case *types.Named, *types.Struct:
|
|
|
|
// TODO(adonovan): use o.imp.Config().TypeChecker.Sizes when
|
|
|
|
// we add the Config() method (needs some thought).
|
|
|
|
szs := types.StdSizes{8, 8}
|
|
|
|
description = fmt.Sprintf("%s (size %d, align %d)", description,
|
|
|
|
szs.Sizeof(t), szs.Alignof(t))
|
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
return &describeTypeResult{
|
2013-11-15 07:22:16 -07:00
|
|
|
qpos: qpos,
|
2013-09-03 13:29:02 -06:00
|
|
|
node: path[0],
|
2013-11-20 14:00:23 -07:00
|
|
|
description: description,
|
2013-09-03 13:29:02 -06:00
|
|
|
typ: t,
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
methods: accessibleMethods(t, qpos.info.Pkg),
|
2013-09-03 13:29:02 -06:00
|
|
|
}, nil
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type describeTypeResult struct {
|
2013-11-15 07:22:16 -07:00
|
|
|
qpos *QueryPos
|
2013-08-27 15:58:26 -06:00
|
|
|
node ast.Node
|
|
|
|
description string
|
|
|
|
typ types.Type
|
2013-09-03 13:29:02 -06:00
|
|
|
methods []*types.Selection
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describeTypeResult) display(printf printfFunc) {
|
|
|
|
printf(r.node, "%s", r.description)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
// Show the underlying type for a reference to a named type.
|
|
|
|
if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() {
|
2013-11-15 07:22:16 -07:00
|
|
|
printf(nt.Obj(), "defined as %s", r.qpos.TypeString(nt.Underlying()))
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Print the method set, if the type kind is capable of bearing methods.
|
|
|
|
switch r.typ.(type) {
|
|
|
|
case *types.Interface, *types.Struct, *types.Named:
|
2013-09-03 13:29:02 -06:00
|
|
|
if len(r.methods) > 0 {
|
|
|
|
printf(r.node, "Method set:")
|
|
|
|
for _, meth := range r.methods {
|
2013-11-15 10:35:11 -07:00
|
|
|
printf(meth.Obj(), "\t%s", r.qpos.SelectionString(meth))
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
} else {
|
2013-09-03 13:29:02 -06:00
|
|
|
printf(r.node, "No methods.")
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describeTypeResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
2013-09-03 13:29:02 -06:00
|
|
|
var namePos, nameDef string
|
|
|
|
if nt, ok := r.typ.(*types.Named); ok {
|
|
|
|
namePos = fset.Position(nt.Obj().Pos()).String()
|
|
|
|
nameDef = nt.Underlying().String()
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
res.Describe = &serial.Describe{
|
2013-09-03 13:29:02 -06:00
|
|
|
Desc: r.description,
|
|
|
|
Pos: fset.Position(r.node.Pos()).String(),
|
|
|
|
Detail: "type",
|
2013-09-24 13:08:14 -06:00
|
|
|
Type: &serial.DescribeType{
|
2013-11-15 07:22:16 -07:00
|
|
|
Type: r.qpos.TypeString(r.typ),
|
2013-09-03 13:29:02 -06:00
|
|
|
NamePos: namePos,
|
|
|
|
NameDef: nameDef,
|
2013-11-15 10:35:11 -07:00
|
|
|
Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset),
|
2013-09-03 13:29:02 -06:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
// ---- PACKAGE ------------------------------------------------------------
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
func describePackage(o *Oracle, qpos *QueryPos, path []ast.Node) (*describePackageResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
var description string
|
2013-09-10 12:19:11 -06:00
|
|
|
var pkg *types.Package
|
2013-08-27 15:58:26 -06:00
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.ImportSpec:
|
2013-12-13 08:04:55 -07:00
|
|
|
pkgname := qpos.info.ImportSpecPkg(n)
|
|
|
|
description = fmt.Sprintf("import of package %q", pkgname.Name())
|
|
|
|
pkg = pkgname.Pkg()
|
2013-08-27 15:58:26 -06:00
|
|
|
|
|
|
|
case *ast.Ident:
|
|
|
|
if _, isDef := path[1].(*ast.File); isDef {
|
2013-09-06 16:13:57 -06:00
|
|
|
// e.g. package id
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
pkg = qpos.info.Pkg
|
2013-09-10 12:19:11 -06:00
|
|
|
description = fmt.Sprintf("definition of package %q", pkg.Path())
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
2013-09-06 16:13:57 -06:00
|
|
|
// e.g. import id
|
|
|
|
// or id.F()
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
pkg = qpos.info.ObjectOf(n).Pkg()
|
2013-09-10 12:19:11 -06:00
|
|
|
description = fmt.Sprintf("reference to package %q", pkg.Path())
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Unreachable?
|
2013-09-25 12:34:39 -06:00
|
|
|
return nil, fmt.Errorf("unexpected AST for package: %T", n)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
var members []*describeMember
|
2013-09-10 12:19:11 -06:00
|
|
|
// NB: "unsafe" has no types.Package
|
2013-09-06 16:13:57 -06:00
|
|
|
if pkg != nil {
|
2013-09-10 12:19:11 -06:00
|
|
|
// Enumerate the accessible package members
|
|
|
|
// in lexicographic order.
|
|
|
|
for _, name := range pkg.Scope().Names() {
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
if pkg == qpos.info.Pkg || ast.IsExported(name) {
|
2013-09-10 12:19:11 -06:00
|
|
|
mem := pkg.Scope().Lookup(name)
|
|
|
|
var methods []*types.Selection
|
|
|
|
if mem, ok := mem.(*types.TypeName); ok {
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
methods = accessibleMethods(mem.Type(), qpos.info.Pkg)
|
2013-09-10 12:19:11 -06:00
|
|
|
}
|
|
|
|
members = append(members, &describeMember{
|
|
|
|
mem,
|
|
|
|
methods,
|
|
|
|
})
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-12-13 08:04:55 -07:00
|
|
|
return &describePackageResult{o.fset, path[0], description, pkg, members}, nil
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type describePackageResult struct {
|
2013-09-03 13:29:02 -06:00
|
|
|
fset *token.FileSet
|
2013-08-27 15:58:26 -06:00
|
|
|
node ast.Node
|
|
|
|
description string
|
2013-09-10 12:19:11 -06:00
|
|
|
pkg *types.Package
|
2013-09-03 13:29:02 -06:00
|
|
|
members []*describeMember // in lexicographic name order
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
type describeMember struct {
|
2013-09-10 12:19:11 -06:00
|
|
|
obj types.Object
|
2013-09-03 13:29:02 -06:00
|
|
|
methods []*types.Selection // in types.MethodSet order
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describePackageResult) display(printf printfFunc) {
|
|
|
|
printf(r.node, "%s", r.description)
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
// Compute max width of name "column".
|
|
|
|
maxname := 0
|
|
|
|
for _, mem := range r.members {
|
2013-09-10 12:19:11 -06:00
|
|
|
if l := len(mem.obj.Name()); l > maxname {
|
2013-09-03 13:29:02 -06:00
|
|
|
maxname = l
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, mem := range r.members {
|
2013-09-10 12:19:11 -06:00
|
|
|
printf(mem.obj, "\t%s", formatMember(mem.obj, maxname))
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, meth := range mem.methods {
|
2013-12-13 08:04:55 -07:00
|
|
|
printf(meth.Obj(), "\t\t%s", types.SelectionString(r.pkg, meth))
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
func formatMember(obj types.Object, maxname int) string {
|
2013-08-27 15:58:26 -06:00
|
|
|
var buf bytes.Buffer
|
2013-09-10 12:19:11 -06:00
|
|
|
fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
|
|
|
|
switch obj := obj.(type) {
|
|
|
|
case *types.Const:
|
2013-11-15 10:35:11 -07:00
|
|
|
fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Pkg(), obj.Type()), obj.Val().String())
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
case *types.Func:
|
2013-11-15 10:35:11 -07:00
|
|
|
fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type()))
|
2013-08-27 15:58:26 -06:00
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
case *types.TypeName:
|
2013-08-27 15:58:26 -06:00
|
|
|
// Abbreviate long aggregate type names.
|
|
|
|
var abbrev string
|
2013-09-10 12:19:11 -06:00
|
|
|
switch t := obj.Type().Underlying().(type) {
|
2013-08-27 15:58:26 -06:00
|
|
|
case *types.Interface:
|
|
|
|
if t.NumMethods() > 1 {
|
|
|
|
abbrev = "interface{...}"
|
|
|
|
}
|
|
|
|
case *types.Struct:
|
|
|
|
if t.NumFields() > 1 {
|
|
|
|
abbrev = "struct{...}"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if abbrev == "" {
|
2013-11-15 10:35:11 -07:00
|
|
|
fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type().Underlying()))
|
2013-08-27 15:58:26 -06:00
|
|
|
} else {
|
|
|
|
fmt.Fprintf(&buf, " %s", abbrev)
|
|
|
|
}
|
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
case *types.Var:
|
2013-11-15 10:35:11 -07:00
|
|
|
fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type()))
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describePackageResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
|
|
|
var members []*serial.DescribeMember
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, mem := range r.members {
|
2013-09-10 12:19:11 -06:00
|
|
|
typ := mem.obj.Type()
|
2013-09-03 13:29:02 -06:00
|
|
|
var val string
|
2013-09-10 12:19:11 -06:00
|
|
|
switch mem := mem.obj.(type) {
|
|
|
|
case *types.Const:
|
|
|
|
val = mem.Val().String()
|
|
|
|
case *types.TypeName:
|
2013-09-03 13:29:02 -06:00
|
|
|
typ = typ.Underlying()
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
members = append(members, &serial.DescribeMember{
|
2013-09-10 12:19:11 -06:00
|
|
|
Name: mem.obj.Name(),
|
2013-09-03 13:29:02 -06:00
|
|
|
Type: typ.String(),
|
|
|
|
Value: val,
|
2013-09-10 12:19:11 -06:00
|
|
|
Pos: fset.Position(mem.obj.Pos()).String(),
|
|
|
|
Kind: tokenOf(mem.obj),
|
2013-11-15 10:35:11 -07:00
|
|
|
Methods: methodsToSerial(r.pkg, mem.methods, fset),
|
2013-09-03 13:29:02 -06:00
|
|
|
})
|
|
|
|
}
|
2013-09-24 13:08:14 -06:00
|
|
|
res.Describe = &serial.Describe{
|
2013-09-03 13:29:02 -06:00
|
|
|
Desc: r.description,
|
|
|
|
Pos: fset.Position(r.node.Pos()).String(),
|
|
|
|
Detail: "package",
|
2013-09-24 13:08:14 -06:00
|
|
|
Package: &serial.DescribePackage{
|
2013-09-10 12:19:11 -06:00
|
|
|
Path: r.pkg.Path(),
|
2013-09-03 13:29:02 -06:00
|
|
|
Members: members,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-10 12:19:11 -06:00
|
|
|
func tokenOf(o types.Object) string {
|
|
|
|
switch o.(type) {
|
|
|
|
case *types.Func:
|
|
|
|
return "func"
|
|
|
|
case *types.Var:
|
|
|
|
return "var"
|
|
|
|
case *types.TypeName:
|
|
|
|
return "type"
|
|
|
|
case *types.Const:
|
|
|
|
return "const"
|
2013-09-13 10:52:57 -06:00
|
|
|
case *types.PkgName:
|
2013-09-10 12:19:11 -06:00
|
|
|
return "package"
|
|
|
|
}
|
|
|
|
panic(o)
|
|
|
|
}
|
|
|
|
|
2013-08-27 15:58:26 -06:00
|
|
|
// ---- STATEMENT ------------------------------------------------------------
|
|
|
|
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
func describeStmt(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeStmtResult, error) {
|
2013-08-27 15:58:26 -06:00
|
|
|
var description string
|
|
|
|
switch n := path[0].(type) {
|
|
|
|
case *ast.Ident:
|
go.tools/oracle: refactor Oracle API to allow repeated queries on same scope.
The existing standalone Query function builds an importer, ssa.Program, oracle,
and query position, executes the query and returns the result.
For clients (such as Frederik Zipp's web-based github.com/fzipp/pythia tool)
that wish to load the program once and make several queries, we now expose
these as separate operations too. Here's a client, in pseudocode:
o := oracle.New(...)
for ... {
qpos := o.ParseQueryPos(...)
res := o.Query(mode, qpos)
print result
}
NB: this is a slight deoptimisation in the one-shot case since we have to
build the entire SSA program with debug info, not just the query package,
since we now don't know the query package at that time.
The 'exact' param to ParseQueryPos needs more thought since its
ideal value is a function of the query mode. This will do for now.
Details:
- expose Oracle type, New() func and Query() method.
- expose QueryPos type and ParseQueryPos func.
- improved package doc comment.
- un-exposed the "needs" bits.
- added test.
R=crawshaw
CC=frederik.zipp, golang-dev
https://golang.org/cl/13810043
2013-09-23 13:02:18 -06:00
|
|
|
if qpos.info.ObjectOf(n).Pos() == n.Pos() {
|
2013-08-27 15:58:26 -06:00
|
|
|
description = "labelled statement"
|
|
|
|
} else {
|
|
|
|
description = "reference to labelled statement"
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Nothing much to say about statements.
|
2013-12-09 07:36:29 -07:00
|
|
|
description = astutil.NodeDescription(n)
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
2013-12-13 08:04:55 -07:00
|
|
|
return &describeStmtResult{o.fset, path[0], description}, nil
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type describeStmtResult struct {
|
2013-09-03 13:29:02 -06:00
|
|
|
fset *token.FileSet
|
2013-08-27 15:58:26 -06:00
|
|
|
node ast.Node
|
|
|
|
description string
|
|
|
|
}
|
|
|
|
|
2013-09-03 13:29:02 -06:00
|
|
|
func (r *describeStmtResult) display(printf printfFunc) {
|
|
|
|
printf(r.node, "%s", r.description)
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:08:14 -06:00
|
|
|
func (r *describeStmtResult) toSerial(res *serial.Result, fset *token.FileSet) {
|
|
|
|
res.Describe = &serial.Describe{
|
2013-09-03 13:29:02 -06:00
|
|
|
Desc: r.description,
|
|
|
|
Pos: fset.Position(r.node.Pos()).String(),
|
|
|
|
Detail: "unknown",
|
|
|
|
}
|
2013-08-27 15:58:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------- Utilities -------------------
|
|
|
|
|
|
|
|
// pathToString returns a string containing the concrete types of the
|
|
|
|
// nodes in path.
|
2013-12-13 08:04:55 -07:00
|
|
|
func pathToString(path []ast.Node) string {
|
2013-08-27 15:58:26 -06:00
|
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprint(&buf, "[")
|
|
|
|
for i, n := range path {
|
|
|
|
if i > 0 {
|
|
|
|
fmt.Fprint(&buf, " ")
|
|
|
|
}
|
|
|
|
fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast."))
|
|
|
|
}
|
|
|
|
fmt.Fprint(&buf, "]")
|
|
|
|
return buf.String()
|
|
|
|
}
|
2013-09-03 13:29:02 -06:00
|
|
|
|
|
|
|
func accessibleMethods(t types.Type, from *types.Package) []*types.Selection {
|
|
|
|
var methods []*types.Selection
|
|
|
|
for _, meth := range ssa.IntuitiveMethodSet(t) {
|
|
|
|
if isAccessibleFrom(meth.Obj(), from) {
|
|
|
|
methods = append(methods, meth)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return methods
|
|
|
|
}
|
|
|
|
|
|
|
|
func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
|
|
|
|
return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
|
|
|
|
}
|
|
|
|
|
2013-11-15 10:35:11 -07:00
|
|
|
func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod {
|
2013-09-24 13:08:14 -06:00
|
|
|
var jmethods []serial.DescribeMethod
|
2013-09-03 13:29:02 -06:00
|
|
|
for _, meth := range methods {
|
2013-09-24 13:08:14 -06:00
|
|
|
jmethods = append(jmethods, serial.DescribeMethod{
|
2013-11-15 10:35:11 -07:00
|
|
|
Name: types.SelectionString(this, meth),
|
2013-09-03 13:29:02 -06:00
|
|
|
Pos: fset.Position(meth.Obj().Pos()).String(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return jmethods
|
|
|
|
}
|