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:
parent
118786e3d6
commit
41fb587948
|
@ -115,6 +115,12 @@ type Info struct {
|
|||
// *ast.Field anonymous struct field or parameter *Var
|
||||
//
|
||||
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
|
||||
|
|
|
@ -356,8 +356,8 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) {
|
|||
goto Error
|
||||
}
|
||||
|
||||
// TODO(gri) don't create a lookupResult if no Objects map exists
|
||||
check.recordObject(arg.Sel, lookupResult(base, obj, index, indirect))
|
||||
// TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
|
||||
check.recordSelection(arg, FieldVal, base, obj, index, false)
|
||||
|
||||
offs := check.conf.offsetof(base, index)
|
||||
x.mode = constant
|
||||
|
|
|
@ -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)
|
||||
goto Error
|
||||
}
|
||||
check.recordObject(e.Sel, exp)
|
||||
check.recordSelection(e, PackageObj, nil, exp, nil, false)
|
||||
// Simplified version of the code for *ast.Idents:
|
||||
// - imported packages use types.Scope and types.Objects
|
||||
// - imported objects are always fully initialized
|
||||
|
@ -225,9 +225,6 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
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 {
|
||||
// method expression
|
||||
m, _ := obj.(*Func)
|
||||
|
@ -242,6 +239,8 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
goto Error
|
||||
}
|
||||
|
||||
check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
|
||||
|
||||
// the receiver type becomes the type of the first function
|
||||
// argument of the method expression's function type
|
||||
var params []*Var
|
||||
|
@ -260,6 +259,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
// regular selector
|
||||
switch obj := obj.(type) {
|
||||
case *Var:
|
||||
check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
|
||||
x.mode = variable
|
||||
x.typ = obj.typ
|
||||
|
||||
|
@ -276,6 +276,8 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
goto Error
|
||||
}
|
||||
|
||||
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
|
||||
|
||||
if debug {
|
||||
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
|
||||
typ := x.typ
|
||||
|
|
|
@ -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.
|
||||
type bailout struct{}
|
||||
|
||||
|
|
|
@ -11,26 +11,6 @@ package types
|
|||
// types always have only one representation (even when imported
|
||||
// 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
|
||||
// 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
|
||||
|
|
|
@ -13,8 +13,78 @@ import (
|
|||
"sync"
|
||||
)
|
||||
|
||||
// TODO(gri) Move Method to objects.go?
|
||||
// TODO(gri) Method.Type() returns the wrong receiver type.
|
||||
// SelectorKind describes the kind of a selector expression recv.obj.
|
||||
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
|
||||
// and corresponding path. The method belongs to the method set of x.
|
||||
|
|
Loading…
Reference in New Issue