1
0
mirror of https://github.com/golang/go synced 2024-11-18 12:24:48 -07:00

go.tools/go/loader: use new types.TypeAndValue mode predicates.

PackageInfo:
- deleted IsType
- inlined + deleted: ValueOf, TypeCaseVar, ImportSpecPkg
- on failure, TypeOf accessor now returns nil (was: panic)

go/ssa: avoid extra map lookups by using Uses or Defs directly when safe to do so,
and keeping the TypeAndValue around in expr0().

LGTM=gri
R=gri, pcc
CC=golang-codereviews
https://golang.org/cl/107650043
This commit is contained in:
Alan Donovan 2014-07-11 10:50:09 +01:00
parent 961ab3ca8d
commit f2db24a319
10 changed files with 84 additions and 137 deletions

View File

@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/types" "code.google.com/p/go.tools/go/types"
) )
@ -32,90 +31,29 @@ func (info *PackageInfo) String() string {
return fmt.Sprintf("PackageInfo(%s)", info.Pkg.Path()) return fmt.Sprintf("PackageInfo(%s)", info.Pkg.Path())
} }
// TypeOf returns the type of expression e. // TODO(gri): move the methods below to types.Info.
// Precondition: e belongs to the package's ASTs.
// // TypeOf returns the type of expression e, or nil if not found.
func (info *PackageInfo) TypeOf(e ast.Expr) types.Type { func (info *PackageInfo) TypeOf(e ast.Expr) types.Type {
if t, ok := info.Types[e]; ok { if t, ok := info.Types[e]; ok {
return t.Type return t.Type
} }
// Defining ast.Idents (id := expr) get only Ident callbacks // Idents appear only in Defs/Uses, not Types.
// but not Expr callbacks.
if id, ok := e.(*ast.Ident); ok { if id, ok := e.(*ast.Ident); ok {
return info.ObjectOf(id).Type() return info.ObjectOf(id).Type()
} }
panic("no type for expression") return nil
} }
// ValueOf returns the value of expression e if it is a constant, nil // ObjectOf returns the type-checker object denoted by the specified
// otherwise. // id, or nil if not found.
// Precondition: e belongs to the package's ASTs.
// //
func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value { // If id is an anonymous struct field, ObjectOf returns the field
return info.Types[e].Value // (*types.Var) it uses, not the type (*types.TypeName) it defines.
}
// ObjectOf returns the typechecker object denoted by the specified id.
//
// If id is an anonymous struct field, the field (*types.Var) is
// returned, not the type (*types.TypeName).
//
// Precondition: id belongs to the package's ASTs.
// //
func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object { func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object {
obj, ok := info.Defs[id] if obj, ok := info.Defs[id]; ok {
if ok {
return obj return obj
} }
return info.Uses[id] return info.Uses[id]
} }
// IsType returns true iff expression e denotes a type.
// Precondition: e belongs to the package's ASTs.
//
// TODO(gri): move this into go/types.
//
func (info *PackageInfo) IsType(e ast.Expr) bool {
switch e := e.(type) {
case *ast.SelectorExpr: // pkg.Type
if _, ok := info.Selections[e]; !ok {
// qualified identifier
_, isType := info.Uses[e.Sel].(*types.TypeName)
return isType
}
case *ast.StarExpr: // *T
return info.IsType(e.X)
case *ast.Ident:
_, isType := info.ObjectOf(e).(*types.TypeName)
return isType
case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
return true
case *ast.ParenExpr:
return info.IsType(e.X)
}
return false
}
// TypeCaseVar returns the implicit variable created by a single-type
// case clause in a type switch, or nil if not found.
//
func (info *PackageInfo) TypeCaseVar(cc *ast.CaseClause) *types.Var {
if v := info.Implicits[cc]; v != nil {
return v.(*types.Var)
}
return nil
}
// ImportSpecPkg returns the PkgName for a given ImportSpec, possibly
// an implicit one for a dot-import or an import-without-rename.
// It returns nil if not found.
//
func (info *PackageInfo) ImportSpecPkg(spec *ast.ImportSpec) *types.PkgName {
if spec.Name != nil {
return info.ObjectOf(spec.Name).(*types.PkgName)
}
if p := info.Implicits[spec]; p != nil {
return p.(*types.PkgName)
}
return nil
}

View File

@ -446,19 +446,23 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr, isZero bool)
// to fn and returning the Value defined by the expression. // to fn and returning the Value defined by the expression.
// //
func (b *builder) expr(fn *Function, e ast.Expr) Value { func (b *builder) expr(fn *Function, e ast.Expr) Value {
// Is expression a constant?
if v := fn.Pkg.info.ValueOf(e); v != nil {
return NewConst(v, fn.Pkg.typeOf(e))
}
e = unparen(e) e = unparen(e)
v := b.expr0(fn, e)
tv := fn.Pkg.info.Types[e]
// Is expression a constant?
if tv.Value != nil {
return NewConst(tv.Value, tv.Type)
}
v := b.expr0(fn, e, tv)
if fn.debugInfo() { if fn.debugInfo() {
emitDebugRef(fn, e, v, false) emitDebugRef(fn, e, v, false)
} }
return v return v
} }
func (b *builder) expr0(fn *Function, e ast.Expr) Value { func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
switch e := e.(type) { switch e := e.(type) {
case *ast.BasicLit: case *ast.BasicLit:
panic("non-constant BasicLit") // unreachable panic("non-constant BasicLit") // unreachable
@ -479,7 +483,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
return fn2 return fn2
} }
v := &MakeClosure{Fn: fn2} v := &MakeClosure{Fn: fn2}
v.setType(fn.Pkg.typeOf(e)) v.setType(tv.Type)
for _, fv := range fn2.FreeVars { for _, fv := range fn2.FreeVars {
v.Bindings = append(v.Bindings, fv.outer) v.Bindings = append(v.Bindings, fv.outer)
fv.outer = nil fv.outer = nil
@ -487,14 +491,13 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
return fn.emit(v) return fn.emit(v)
case *ast.TypeAssertExpr: // single-result form only case *ast.TypeAssertExpr: // single-result form only
return emitTypeAssert(fn, b.expr(fn, e.X), fn.Pkg.typeOf(e), e.Lparen) return emitTypeAssert(fn, b.expr(fn, e.X), tv.Type, e.Lparen)
case *ast.CallExpr: case *ast.CallExpr:
typ := fn.Pkg.typeOf(e) if fn.Pkg.info.Types[e.Fun].IsType() {
if fn.Pkg.info.IsType(e.Fun) {
// Explicit type conversion, e.g. string(x) or big.Int(x) // Explicit type conversion, e.g. string(x) or big.Int(x)
x := b.expr(fn, e.Args[0]) x := b.expr(fn, e.Args[0])
y := emitConv(fn, x, typ) y := emitConv(fn, x, tv.Type)
if y != x { if y != x {
switch y := y.(type) { switch y := y.(type) {
case *Convert: case *Convert:
@ -509,8 +512,8 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
} }
// Call to "intrinsic" built-ins, e.g. new, make, panic. // Call to "intrinsic" built-ins, e.g. new, make, panic.
if id, ok := unparen(e.Fun).(*ast.Ident); ok { if id, ok := unparen(e.Fun).(*ast.Ident); ok {
if obj, ok := fn.Pkg.objectOf(id).(*types.Builtin); ok { if obj, ok := fn.Pkg.info.Uses[id].(*types.Builtin); ok {
if v := b.builtin(fn, obj, e.Args, typ, e.Lparen); v != nil { if v := b.builtin(fn, obj, e.Args, tv.Type, e.Lparen); v != nil {
return v return v
} }
} }
@ -518,7 +521,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
// Regular function call. // Regular function call.
var v Call var v Call
b.setCall(fn, e, &v.Call) b.setCall(fn, e, &v.Call)
v.setType(typ) v.setType(tv.Type)
return fn.emit(&v) return fn.emit(&v)
case *ast.UnaryExpr: case *ast.UnaryExpr:
@ -541,7 +544,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
X: b.expr(fn, e.X), X: b.expr(fn, e.X),
} }
v.setPos(e.OpPos) v.setPos(e.OpPos)
v.setType(fn.Pkg.typeOf(e)) v.setType(tv.Type)
return fn.emit(v) return fn.emit(v)
default: default:
panic(e.Op) panic(e.Op)
@ -554,12 +557,12 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
case token.SHL, token.SHR: case token.SHL, token.SHR:
fallthrough fallthrough
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT: case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), fn.Pkg.typeOf(e), e.OpPos) return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), tv.Type, e.OpPos)
case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ: case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ:
cmp := emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e.OpPos) cmp := emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e.OpPos)
// The type of x==y may be UntypedBool. // The type of x==y may be UntypedBool.
return emitConv(fn, cmp, DefaultType(fn.Pkg.typeOf(e))) return emitConv(fn, cmp, DefaultType(tv.Type))
default: default:
panic("illegal op in BinaryExpr: " + e.Op.String()) panic("illegal op in BinaryExpr: " + e.Op.String())
} }
@ -592,17 +595,17 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
Max: max, Max: max,
} }
v.setPos(e.Lbrack) v.setPos(e.Lbrack)
v.setType(fn.Pkg.typeOf(e)) v.setType(tv.Type)
return fn.emit(v) return fn.emit(v)
case *ast.Ident: case *ast.Ident:
obj := fn.Pkg.objectOf(e) obj := fn.Pkg.info.Uses[e]
// Universal built-in or nil? // Universal built-in or nil?
switch obj := obj.(type) { switch obj := obj.(type) {
case *types.Builtin: case *types.Builtin:
return &Builtin{name: obj.Name(), sig: fn.Pkg.typeOf(e).(*types.Signature)} return &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)}
case *types.Nil: case *types.Nil:
return nilConst(fn.Pkg.typeOf(e)) return nilConst(tv.Type)
} }
// Package-level func or var? // Package-level func or var?
if v := fn.Prog.packageLevelValue(obj); v != nil { if v := fn.Prog.packageLevelValue(obj); v != nil {
@ -624,7 +627,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
case types.MethodExpr: case types.MethodExpr:
// (*T).f or T.f, the method f from the method-set of type T. // (*T).f or T.f, the method f from the method-set of type T.
// The result is a "thunk". // The result is a "thunk".
return emitConv(fn, makeThunk(fn.Prog, sel), fn.Pkg.typeOf(e)) return emitConv(fn, makeThunk(fn.Prog, sel), tv.Type)
case types.MethodVal: case types.MethodVal:
// e.f where e is an expression and f is a method. // e.f where e is an expression and f is a method.
@ -645,7 +648,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr) Value {
Bindings: []Value{v}, Bindings: []Value{v},
} }
c.setPos(e.Sel.Pos()) c.setPos(e.Sel.Pos())
c.setType(fn.Pkg.typeOf(e)) c.setType(tv.Type)
return fn.emit(c) return fn.emit(c)
case types.FieldVal: case types.FieldVal:
@ -952,10 +955,7 @@ func (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) {
var lval lvalue = blank{} var lval lvalue = blank{}
if !isBlankIdent(lhs) { if !isBlankIdent(lhs) {
if isDef { if isDef {
// Local may be "redeclared" in the same if obj := fn.Pkg.info.Defs[lhs.(*ast.Ident)]; obj != nil {
// scope, so don't blindly create anew.
obj := fn.Pkg.objectOf(lhs.(*ast.Ident))
if _, ok := fn.objects[obj]; !ok {
fn.addNamedLocal(obj) fn.addNamedLocal(obj)
} }
} }
@ -1310,7 +1310,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
} }
func (b *builder) typeCaseBody(fn *Function, cc *ast.CaseClause, x Value, done *BasicBlock) { func (b *builder) typeCaseBody(fn *Function, cc *ast.CaseClause, x Value, done *BasicBlock) {
if obj := fn.Pkg.info.TypeCaseVar(cc); obj != nil { if obj := fn.Pkg.info.Implicits[cc]; obj != nil {
// In a switch y := x.(type), each case clause // In a switch y := x.(type), each case clause
// implicitly declares a distinct object y. // implicitly declares a distinct object y.
// In a single-type case, y has that type. // In a single-type case, y has that type.
@ -2115,7 +2115,7 @@ func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
v.setType(types.NewTuple()) v.setType(types.NewTuple())
pkg.init.emit(&v) pkg.init.emit(&v)
} else { } else {
fn = pkg.values[pkg.objectOf(id)].(*Function) fn = pkg.values[pkg.info.Defs[id]].(*Function)
} }
b.buildFunction(fn) b.buildFunction(fn)
} }
@ -2250,6 +2250,7 @@ func (p *Package) Build() {
} }
} }
// Like ObjectOf, but panics instead of returning nil.
// Only valid during p's create and build phases. // Only valid during p's create and build phases.
func (p *Package) objectOf(id *ast.Ident) types.Object { func (p *Package) objectOf(id *ast.Ident) types.Object {
if o := p.info.ObjectOf(id); o != nil { if o := p.info.ObjectOf(id); o != nil {
@ -2259,9 +2260,14 @@ func (p *Package) objectOf(id *ast.Ident) types.Object {
id.Name, p.Prog.Fset.Position(id.Pos()))) id.Name, p.Prog.Fset.Position(id.Pos())))
} }
// Like TypeOf, but panics instead of returning nil.
// Only valid during p's create and build phases. // Only valid during p's create and build phases.
func (p *Package) typeOf(e ast.Expr) types.Type { func (p *Package) typeOf(e ast.Expr) types.Type {
return p.info.TypeOf(e) if T := p.info.TypeOf(e); T != nil {
return T
}
panic(fmt.Sprintf("no type for %T @ %s",
e, p.Prog.Fset.Position(e.Pos())))
} }
// needMethodsOf ensures that runtime type information (including the // needMethodsOf ensures that runtime type information (including the

View File

@ -131,7 +131,7 @@ func membersFromDecl(pkg *Package, decl ast.Decl) {
for _, spec := range decl.Specs { for _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names { for _, id := range spec.(*ast.ValueSpec).Names {
if !isBlankIdent(id) { if !isBlankIdent(id) {
memberFromObject(pkg, pkg.objectOf(id), nil) memberFromObject(pkg, pkg.info.Defs[id], nil)
} }
} }
} }
@ -140,7 +140,7 @@ func membersFromDecl(pkg *Package, decl ast.Decl) {
for _, spec := range decl.Specs { for _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names { for _, id := range spec.(*ast.ValueSpec).Names {
if !isBlankIdent(id) { if !isBlankIdent(id) {
memberFromObject(pkg, pkg.objectOf(id), spec) memberFromObject(pkg, pkg.info.Defs[id], spec)
} }
} }
} }
@ -149,7 +149,7 @@ func membersFromDecl(pkg *Package, decl ast.Decl) {
for _, spec := range decl.Specs { for _, spec := range decl.Specs {
id := spec.(*ast.TypeSpec).Name id := spec.(*ast.TypeSpec).Name
if !isBlankIdent(id) { if !isBlankIdent(id) {
memberFromObject(pkg, pkg.objectOf(id), nil) memberFromObject(pkg, pkg.info.Defs[id], nil)
} }
} }
} }
@ -160,7 +160,7 @@ func membersFromDecl(pkg *Package, decl ast.Decl) {
return // no object return // no object
} }
if !isBlankIdent(id) { if !isBlankIdent(id) {
memberFromObject(pkg, pkg.objectOf(id), decl) memberFromObject(pkg, pkg.info.Defs[id], decl)
} }
} }
} }

View File

@ -224,7 +224,7 @@ func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.Func
if recv != nil { if recv != nil {
for _, field := range recv.List { for _, field := range recv.List {
for _, n := range field.Names { for _, n := range field.Names {
f.addSpilledParam(f.Pkg.objectOf(n)) f.addSpilledParam(f.Pkg.info.Defs[n])
} }
// Anonymous receiver? No need to spill. // Anonymous receiver? No need to spill.
if field.Names == nil { if field.Names == nil {
@ -238,7 +238,7 @@ func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.Func
n := len(f.Params) // 1 if has recv, 0 otherwise n := len(f.Params) // 1 if has recv, 0 otherwise
for _, field := range functype.Params.List { for _, field := range functype.Params.List {
for _, n := range field.Names { for _, n := range field.Names {
f.addSpilledParam(f.Pkg.objectOf(n)) f.addSpilledParam(f.Pkg.info.Defs[n])
} }
// Anonymous parameter? No need to spill. // Anonymous parameter? No need to spill.
if field.Names == nil { if field.Names == nil {
@ -307,7 +307,7 @@ func (f *Function) finishBody() {
f.syntax = extentNode{n.Pos(), n.End()} f.syntax = extentNode{n.Pos(), n.End()}
} }
// Remove any f.Locals that are now heap-allocated. // Remove from f.Locals any Allocs that escape to the heap.
j := 0 j := 0
for _, l := range f.Locals { for _, l := range f.Locals {
if !l.Heap { if !l.Heap {
@ -393,7 +393,7 @@ func (f *Function) addNamedLocal(obj types.Object) *Alloc {
} }
func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc { func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc {
return f.addNamedLocal(f.Pkg.objectOf(id)) return f.addNamedLocal(f.Pkg.info.Defs[id])
} }
// addLocal creates an anonymous local variable of type typ, adds it // addLocal creates an anonymous local variable of type typ, adds it

View File

@ -305,7 +305,7 @@ type Function struct {
Prog *Program // enclosing program Prog *Program // enclosing program
Params []*Parameter // function parameters; for methods, includes receiver Params []*Parameter // function parameters; for methods, includes receiver
FreeVars []*FreeVar // free variables whose values must be supplied by closure FreeVars []*FreeVar // free variables whose values must be supplied by closure
Locals []*Alloc Locals []*Alloc // local variables of this function
Blocks []*BasicBlock // basic blocks of the function; nil => external Blocks []*BasicBlock // basic blocks of the function; nil => external
Recover *BasicBlock // optional; control transfers here after recovered panic Recover *BasicBlock // optional; control transfers here after recovered panic
AnonFuncs []*Function // anonymous functions directly beneath this one AnonFuncs []*Function // anonymous functions directly beneath this one

View File

@ -38,13 +38,13 @@ func callees(o *Oracle, qpos *QueryPos) (queryResult, error) {
// not what the user intended. // not what the user intended.
// Reject type conversions. // Reject type conversions.
if qpos.info.IsType(e.Fun) { if qpos.info.Types[e.Fun].IsType() {
return nil, fmt.Errorf("this is a type conversion, not a function call") return nil, fmt.Errorf("this is a type conversion, not a function call")
} }
// Reject calls to built-ins. // Reject calls to built-ins.
if id, ok := unparen(e.Fun).(*ast.Ident); ok { if id, ok := unparen(e.Fun).(*ast.Ident); ok {
if b, ok := qpos.info.ObjectOf(id).(*types.Builtin); ok { if b, ok := qpos.info.Uses[id].(*types.Builtin); ok {
return nil, fmt.Errorf("this is a call to the built-in '%s' operator", b.Name()) return nil, fmt.Errorf("this is a call to the built-in '%s' operator", b.Name())
} }
} }

View File

@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"log"
"os" "os"
"strings" "strings"
@ -180,7 +181,8 @@ func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.No
return path, actionExpr return path, actionExpr
case *ast.SelectorExpr: case *ast.SelectorExpr:
if pkginfo.ObjectOf(n.Sel) == nil { // TODO(adonovan): use Selections info directly.
if pkginfo.Uses[n.Sel] == nil {
// TODO(adonovan): is this reachable? // TODO(adonovan): is this reachable?
return path, actionUnknown return path, actionUnknown
} }
@ -267,15 +269,15 @@ func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.No
return path[1:], actionPackage return path[1:], actionPackage
default: default:
// e.g. blank identifier (go/types bug?) // e.g. blank identifier
// or y in "switch y := x.(type)" (go/types bug?) // or y in "switch y := x.(type)"
// or code in a _test.go file that's not part of the package. // or code in a _test.go file that's not part of the package.
fmt.Printf("unknown reference %s in %T\n", n, path[1]) log.Printf("unknown reference %s in %T\n", n, path[1])
return path, actionUnknown return path, actionUnknown
} }
case *ast.StarExpr: case *ast.StarExpr:
if pkginfo.IsType(n) { if pkginfo.Types[n].IsType() {
return path, actionType return path, actionType
} }
return path, actionExpr return path, actionExpr
@ -311,7 +313,7 @@ func describeValue(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeValueRe
} }
typ := qpos.info.TypeOf(expr) typ := qpos.info.TypeOf(expr)
constVal := qpos.info.ValueOf(expr) constVal := qpos.info.Types[expr].Value
return &describeValueResult{ return &describeValueResult{
qpos: qpos, qpos: qpos,
@ -497,7 +499,12 @@ func describePackage(o *Oracle, qpos *QueryPos, path []ast.Node) (*describePacka
var pkg *types.Package var pkg *types.Package
switch n := path[0].(type) { switch n := path[0].(type) {
case *ast.ImportSpec: case *ast.ImportSpec:
pkgname := qpos.info.ImportSpecPkg(n) var pkgname *types.PkgName
if n.Name != nil {
pkgname = qpos.info.Defs[n.Name].(*types.PkgName)
} else if p := qpos.info.Implicits[n]; p != nil {
pkgname = p.(*types.PkgName)
}
description = fmt.Sprintf("import of package %q", pkgname.Pkg().Path()) description = fmt.Sprintf("import of package %q", pkgname.Pkg().Path())
pkg = pkgname.Pkg() pkg = pkgname.Pkg()
@ -507,7 +514,7 @@ func describePackage(o *Oracle, qpos *QueryPos, path []ast.Node) (*describePacka
pkg = qpos.info.Pkg pkg = qpos.info.Pkg
description = fmt.Sprintf("definition of package %q", pkg.Path()) description = fmt.Sprintf("definition of package %q", pkg.Path())
} else { } else {
// e.g. import id // e.g. import id "..."
// or id.F() // or id.F()
pkg = qpos.info.ObjectOf(n).Pkg() pkg = qpos.info.ObjectOf(n).Pkg()
description = fmt.Sprintf("reference to package %q", pkg.Path()) description = fmt.Sprintf("reference to package %q", pkg.Path())
@ -662,7 +669,7 @@ func describeStmt(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeStmtResu
var description string var description string
switch n := path[0].(type) { switch n := path[0].(type) {
case *ast.Ident: case *ast.Ident:
if qpos.info.ObjectOf(n).Pos() == n.Pos() { if qpos.info.Defs[n] != nil {
description = "labelled statement" description = "labelled statement"
} else { } else {
description = "reference to labelled statement" description = "reference to labelled statement"

View File

@ -51,17 +51,13 @@ func freevars(o *Oracle, qpos *QueryPos) (queryResult, error) {
} }
id = func(n *ast.Ident) types.Object { id = func(n *ast.Ident) types.Object {
obj := qpos.info.ObjectOf(n) obj := qpos.info.Uses[n]
if obj == nil { if obj == nil {
return nil // TODO(adonovan): fix: this fails for *types.Label. return nil // not a reference
panic("no types.Object for ast.Ident")
} }
if _, ok := obj.(*types.PkgName); ok { if _, ok := obj.(*types.PkgName); ok {
return nil // imported package return nil // imported package
} }
if n.Pos() == obj.Pos() {
return nil // this ident is the definition, not a reference
}
if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) { if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) {
return nil // not defined in this file return nil // not defined in this file
} }

View File

@ -113,7 +113,7 @@ Dot imports are forbidden in the template.
// TODO(adonovan): expand upon the above documentation as an HTML page. // TODO(adonovan): expand upon the above documentation as an HTML page.
// TODO(adonovan): eliminate dependency on loader.PackageInfo. // TODO(adonovan): eliminate dependency on loader.PackageInfo.
// Move its ObjectOf/IsType/TypeOf methods into go/types. // Move its TypeOf method into go/types.
// A Transformer represents a single example-based transformation. // A Transformer represents a single example-based transformation.
type Transformer struct { type Transformer struct {

View File

@ -105,7 +105,7 @@ func (tr *Transformer) matchExpr(x, y ast.Expr) bool {
case *ast.CallExpr: case *ast.CallExpr:
y := y.(*ast.CallExpr) y := y.(*ast.CallExpr)
match := tr.matchExpr // function call match := tr.matchExpr // function call
if tr.info.IsType(x.Fun) { if tr.info.Types[x.Fun].IsType() {
match = tr.matchType // type conversion match = tr.matchType // type conversion
} }
return x.Ellipsis.IsValid() == y.Ellipsis.IsValid() && return x.Ellipsis.IsValid() == y.Ellipsis.IsValid() &&