go.tools/go/types: implement Info.Selections

Should make it easier for clients to deal with
selector expressions.

Removed Field type: The respective information
is now reported in Info.Selections.

R=adonovan
CC=golang-dev
https://golang.org/cl/11942043
This commit is contained in:
Robert Griesemer 2013-07-26 13:55:14 -07:00
parent 118786e3d6
commit 41fb587948
6 changed files with 95 additions and 28 deletions

View File

@ -115,6 +115,12 @@ type Info struct {
// *ast.Field anonymous struct field or parameter *Var // *ast.Field anonymous struct field or parameter *Var
// //
Implicits map[ast.Node]Object Implicits map[ast.Node]Object
// If Selections != nil, it records the selector expression and corresponding
// selection, i.e., package object (qualified identifier), struct field (field
// selector), or method (method expression or value) for each selector expression
// that is type-checked.
Selections map[*ast.SelectorExpr]*Selection
} }
// Check type-checks a package and returns the resulting package object, the first // Check type-checks a package and returns the resulting package object, the first

View File

@ -356,8 +356,8 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) {
goto Error goto Error
} }
// TODO(gri) don't create a lookupResult if no Objects map exists // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
check.recordObject(arg.Sel, lookupResult(base, obj, index, indirect)) check.recordSelection(arg, FieldVal, base, obj, index, false)
offs := check.conf.offsetof(base, index) offs := check.conf.offsetof(base, index)
x.mode = constant x.mode = constant

View File

@ -182,7 +182,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
check.errorf(e.Pos(), "%s not exported by package %s", sel, ident) check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
goto Error goto Error
} }
check.recordObject(e.Sel, exp) check.recordSelection(e, PackageObj, nil, exp, nil, false)
// Simplified version of the code for *ast.Idents: // Simplified version of the code for *ast.Idents:
// - imported packages use types.Scope and types.Objects // - imported packages use types.Scope and types.Objects
// - imported objects are always fully initialized // - imported objects are always fully initialized
@ -225,9 +225,6 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
goto Error goto Error
} }
// TODO(gri) don't create a lookupResult if no Objects map exists
check.recordObject(e.Sel, lookupResult(x.typ, obj, index, indirect))
if x.mode == typexpr { if x.mode == typexpr {
// method expression // method expression
m, _ := obj.(*Func) m, _ := obj.(*Func)
@ -242,6 +239,8 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
goto Error goto Error
} }
check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
// the receiver type becomes the type of the first function // the receiver type becomes the type of the first function
// argument of the method expression's function type // argument of the method expression's function type
var params []*Var var params []*Var
@ -260,6 +259,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
// regular selector // regular selector
switch obj := obj.(type) { switch obj := obj.(type) {
case *Var: case *Var:
check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
x.mode = variable x.mode = variable
x.typ = obj.typ x.typ = obj.typ
@ -276,6 +276,8 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
goto Error goto Error
} }
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
if debug { if debug {
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree. // Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
typ := x.typ typ := x.typ

View File

@ -96,6 +96,15 @@ func (check *checker) recordImplicit(node ast.Node, obj Object) {
} }
} }
func (check *checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
assert(obj != nil && (recv == nil || len(index) > 0))
check.recordObject(x.Sel, obj)
// TODO(gri) Should we also call recordTypeAndValue?
if m := check.Selections; m != nil {
m[x] = &Selection{kind, obj, selectorPath{recv, index, indirect}}
}
}
// A bailout panic is raised to indicate early termination. // A bailout panic is raised to indicate early termination.
type bailout struct{} type bailout struct{}

View File

@ -11,26 +11,6 @@ package types
// types always have only one representation (even when imported // types always have only one representation (even when imported
// indirectly via different packages.) // indirectly via different packages.)
// TODO(gri) Move Field to objects.go?
// A Field represents a struct field x.f and corresponding path.
type Field struct {
*Var
selectorPath
}
func lookupResult(typ Type, obj Object, index []int, indirect bool) Object {
switch obj := obj.(type) {
case nil:
return nil
case *Var:
return &Field{obj, selectorPath{typ, index, indirect}}
case *Func:
return &Method{obj, selectorPath{typ, index, indirect}}
}
panic("unreachable")
}
// LookupFieldOrMethod looks up a field or method with given package and name // LookupFieldOrMethod looks up a field or method with given package and name
// in typ and returns the corresponding *Var or *Func, an index sequence, // in typ and returns the corresponding *Var or *Func, an index sequence,
// and a bool indicating if there were any pointer indirections on the path // and a bool indicating if there were any pointer indirections on the path

View File

@ -13,8 +13,78 @@ import (
"sync" "sync"
) )
// TODO(gri) Move Method to objects.go? // SelectorKind describes the kind of a selector expression recv.obj.
// TODO(gri) Method.Type() returns the wrong receiver type. type SelectionKind int
const (
FieldVal SelectionKind = iota // recv.obj is a struct field selector
MethodVal // recv.obj is a method selector
MethodExpr // recv.obj is a method expression
PackageObj // recv.obj is a qualified identifier
)
// A Selection describes a selector expression recv.obj.
type Selection struct {
kind SelectionKind
obj Object
selectorPath // not valid for PackageObj
}
func (s *Selection) Kind() SelectionKind { return s.kind }
func (s *Selection) Obj() Object { return s.obj }
// Type returns the type of the selector recv.obj which may be different
// from the type of the selected object obj.
//
// Given the example declarations:
//
// type T struct{ x int; E }
// type E struct{}
// func (e E) m() {}
// var p *T
//
// the following relationships exist:
//
// Kind Selector Recv type Selection type
//
// FieldVal p.x T int (type of x)
// MethodVal p.m *T func (e *T) m()
// MethodExpr T.m T func m(_ T)
// PackageObj math.Pi nil untyped numeric constant (type of Pi)
//
func (s *Selection) Type() Type {
switch s.kind {
case MethodVal:
// The type of recv.obj is a method with its receiver type
// changed to s.recv.
// TODO(gri) Similar code is already in Method.Type below.
// Method may go away in favor of Selection.
sig := *s.obj.(*Func).typ.(*Signature)
recv := *sig.recv
recv.typ = s.recv
sig.recv = &recv
return &sig
case MethodExpr:
// The type of recv.obj is a function (without receiver)
// and an additional first argument of type recv.
// TODO(gri) Similar code is already in call.go - factor!
sig := *s.obj.(*Func).typ.(*Signature)
arg0 := *sig.recv
arg0.typ = s.recv
var params []*Var
if sig.params != nil {
params = sig.params.vars
}
sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
return &sig
}
// In all other cases, the type of recv.obj is the type of obj.
return s.obj.Type()
}
// TODO(gri) Replace Method in favor of Selection (make Method an interface?).
// A Method represents a concrete or abstract (interface) method x.m // A Method represents a concrete or abstract (interface) method x.m
// and corresponding path. The method belongs to the method set of x. // and corresponding path. The method belongs to the method set of x.