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
|
// *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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue