go.tools/go/types: simplified lookup
- much simpler lookup - more result information - will make tracking of pointer-ness easier TODO: apply the same changes to method set computation R=adonovan CC=golang-dev https://golang.org/cl/10379049
This commit is contained in:
parent
33ae2b030f
commit
9ff248b00d
|
|
@ -342,8 +342,8 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
|
|||
goto Error
|
||||
}
|
||||
sel := arg.Sel.Name
|
||||
_, index, _ := LookupFieldOrMethod(x.typ, check.pkg, arg.Sel.Name)
|
||||
if index == nil {
|
||||
obj, index := LookupFieldOrMethod(x.typ, check.pkg, arg.Sel.Name)
|
||||
if obj == nil {
|
||||
check.invalidArg(x.pos(), "%s has no single field %s", x, sel)
|
||||
goto Error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1311,7 +1311,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||
goto Error
|
||||
}
|
||||
// TODO(gri) use collision information for better error message
|
||||
obj, _, _ := LookupFieldOrMethod(x.typ, check.pkg, sel)
|
||||
obj, _ := LookupFieldOrMethod(x.typ, check.pkg, sel)
|
||||
if obj == nil {
|
||||
check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel)
|
||||
goto Error
|
||||
|
|
|
|||
|
|
@ -889,7 +889,7 @@ func (p *gcParser) parseMethodDecl() {
|
|||
// add method to type unless type was imported before
|
||||
// and method exists already
|
||||
// TODO(gri) This is a quadratic algorithm - ok for now because method counts are small.
|
||||
if lookupMethod(base.methods, pkg, name) == nil {
|
||||
if _, m := lookupMethod(base.methods, pkg, name); m == nil {
|
||||
base.methods = append(base.methods, NewFunc(token.NoPos, pkg, name, sig))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,23 @@ package types
|
|||
|
||||
import "go/ast"
|
||||
|
||||
// LookupFieldOrMethod looks up a field or method with given package and name in typ.
|
||||
// If an entry is found, obj is the corresponding *Field or *Func. For fields, index
|
||||
// is the index sequence to reach the (possibly embedded) field; for methods, index
|
||||
// is nil; and collision is false. If no entry is found, obj is nil, index is undefined,
|
||||
// and collision indicates if the reason for not finding an entry was a name collision.
|
||||
// LookupFieldOrMethod looks up a field or method with given package and name
|
||||
// in typ and returns the corresponding *Field or *Func, and an index sequence.
|
||||
//
|
||||
func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int, collision bool) {
|
||||
// The last index entry is the field or method index in the (possibly embedded)
|
||||
// type where the entry was found, either:
|
||||
//
|
||||
// 1) the list of declared methods of a named type; or
|
||||
// 2) the list of all methods (method set) of an interface type; or
|
||||
// 3) the list of fields of a struct type.
|
||||
//
|
||||
// The earlier index entries are the indices of the embedded fields traversed
|
||||
// to get to the found entry, starting at depth 0.
|
||||
//
|
||||
// If no entry is found, a nil object is returned. In this case, the returned
|
||||
// index sequence points to an ambiguous entry if it exists, or it is nil.
|
||||
//
|
||||
func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index []int) {
|
||||
if name == "_" {
|
||||
return // empty fields/methods are never found
|
||||
}
|
||||
|
|
@ -31,101 +41,61 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
|||
// named types that we have seen already
|
||||
seen := make(map[*Named]bool)
|
||||
|
||||
// We treat the top-most level separately because it's simpler
|
||||
// (no incoming multiples) and because it's the common case.
|
||||
|
||||
if t, _ := typ.(*Named); t != nil {
|
||||
seen[t] = true
|
||||
if m := lookupMethod(t.methods, pkg, name); m != nil {
|
||||
assert(m.typ != nil)
|
||||
return m, nil, false
|
||||
}
|
||||
typ = t.underlying
|
||||
}
|
||||
|
||||
// embedded named types at the current and next lower depth
|
||||
// embedded represents an embedded named type
|
||||
type embedded struct {
|
||||
typ *Named
|
||||
index []int // field index sequence
|
||||
multiples bool
|
||||
}
|
||||
var current, next []embedded
|
||||
|
||||
switch t := typ.(type) {
|
||||
case *Struct:
|
||||
for i, f := range t.fields {
|
||||
if f.isMatch(pkg, name) {
|
||||
assert(f.typ != nil)
|
||||
return f, []int{i}, false
|
||||
}
|
||||
if f.anonymous {
|
||||
// Ignore embedded basic types - only user-defined
|
||||
// named types can have methods or struct fields.
|
||||
if t, _ := f.typ.Deref().(*Named); t != nil {
|
||||
next = append(next, embedded{t, []int{i}, false})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *Interface:
|
||||
if m := lookupMethod(t.methods, pkg, name); m != nil {
|
||||
assert(m.typ != nil)
|
||||
return m, nil, false
|
||||
}
|
||||
typ *Named // nil means use the outer typ variable instead
|
||||
index []int // field/method indices, starting with index at depth 0
|
||||
multiples bool // if set, type appears multiple times at its depth
|
||||
}
|
||||
|
||||
// search the next depth if we don't have a match yet and there's work to do
|
||||
for obj == nil && len(next) > 0 {
|
||||
// Consolidate next: collect multiple entries with the same
|
||||
// type into a single entry marked as containing multiples.
|
||||
n := 0 // number of entries w/ unique type
|
||||
prev := make(map[*Named]int) // index at which type was previously seen
|
||||
for _, e := range next {
|
||||
if i, found := prev[e.typ]; found {
|
||||
next[i].multiples = true
|
||||
// ignore this entry
|
||||
} else {
|
||||
prev[e.typ] = n
|
||||
next[n] = e
|
||||
n++
|
||||
}
|
||||
}
|
||||
// next[:n] is the list of embedded entries to process
|
||||
// Start with typ as single entry at lowest depth.
|
||||
// If typ is not a named type, insert a nil type instead.
|
||||
t, _ := typ.(*Named)
|
||||
current := []embedded{{t, nil, false}}
|
||||
|
||||
// The underlying arrays of current and next are different, thus
|
||||
// swapping is safe and they never share the same underlying array.
|
||||
current, next = next[:n], current[:0] // don't waste underlying array
|
||||
// search current depth if there's work to do
|
||||
for len(current) > 0 {
|
||||
var next []embedded // embedded types found at current depth
|
||||
|
||||
// look for name in all types at this depth
|
||||
// look for (pkg, name) in all types at this depth
|
||||
for _, e := range current {
|
||||
if seen[e.typ] {
|
||||
continue
|
||||
}
|
||||
seen[e.typ] = true
|
||||
|
||||
// look for a matching attached method
|
||||
if m := lookupMethod(e.typ.methods, pkg, name); m != nil {
|
||||
// potential match
|
||||
assert(m.typ != nil)
|
||||
if obj != nil || e.multiples {
|
||||
return nil, nil, true
|
||||
// The very first time only, e.typ may be nil.
|
||||
// In this case, we don't have a named type and
|
||||
// we simply continue with the underlying type.
|
||||
if e.typ != nil {
|
||||
if seen[e.typ] {
|
||||
continue
|
||||
}
|
||||
obj = m
|
||||
index = nil
|
||||
seen[e.typ] = true
|
||||
|
||||
// look for a matching attached method
|
||||
if i, m := lookupMethod(e.typ.methods, pkg, name); m != nil {
|
||||
// potential match
|
||||
assert(m.typ != nil)
|
||||
index = concat(e.index, i)
|
||||
if obj != nil || e.multiples {
|
||||
obj = nil // collision
|
||||
return
|
||||
}
|
||||
obj = m
|
||||
}
|
||||
|
||||
// continue with underlying type
|
||||
typ = e.typ.underlying
|
||||
}
|
||||
|
||||
switch t := e.typ.underlying.(type) {
|
||||
switch t := typ.(type) {
|
||||
case *Struct:
|
||||
// look for a matching field and collect embedded types
|
||||
for i, f := range t.fields {
|
||||
if f.isMatch(pkg, name) {
|
||||
assert(f.typ != nil)
|
||||
index = concat(e.index, i)
|
||||
if obj != nil || e.multiples {
|
||||
return nil, nil, true
|
||||
obj = nil // collision
|
||||
return
|
||||
}
|
||||
obj = f
|
||||
index = append(index[:0], e.index...)
|
||||
index = append(index, i)
|
||||
continue
|
||||
}
|
||||
// Collect embedded struct fields for searching the next
|
||||
|
|
@ -141,29 +111,59 @@ func LookupFieldOrMethod(typ Type, pkg *Package, name string) (obj Object, index
|
|||
// Ignore embedded basic types - only user-defined
|
||||
// named types can have methods or have struct fields.
|
||||
if t, _ := f.typ.Deref().(*Named); t != nil {
|
||||
var copy []int
|
||||
copy = append(copy, e.index...)
|
||||
copy = append(copy, i)
|
||||
next = append(next, embedded{t, copy, e.multiples})
|
||||
next = append(next, embedded{t, concat(e.index, i), e.multiples})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case *Interface:
|
||||
// look for a matching method
|
||||
if m := lookupMethod(t.methods, pkg, name); m != nil {
|
||||
if i, m := lookupMethod(t.methods, pkg, name); m != nil {
|
||||
assert(m.typ != nil)
|
||||
index = concat(e.index, i)
|
||||
if obj != nil || e.multiples {
|
||||
return nil, nil, true
|
||||
obj = nil // collision
|
||||
return
|
||||
}
|
||||
obj = m
|
||||
index = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if obj != nil {
|
||||
return // found a match
|
||||
}
|
||||
|
||||
// Consolidate next: collect multiple entries with the same
|
||||
// type into a single entry marked as containing multiples.
|
||||
n := len(next)
|
||||
if n > 1 {
|
||||
n := 0 // number of entries w/ unique type
|
||||
prev := make(map[*Named]int) // index at which type was previously seen
|
||||
for _, e := range next {
|
||||
if i, found := prev[e.typ]; found {
|
||||
next[i].multiples = true
|
||||
// ignore this entry
|
||||
} else {
|
||||
prev[e.typ] = n
|
||||
next[n] = e
|
||||
n++
|
||||
}
|
||||
}
|
||||
}
|
||||
current = next[:n]
|
||||
}
|
||||
|
||||
return
|
||||
index = nil
|
||||
return // not found
|
||||
}
|
||||
|
||||
// concat returns the result of concatenating list and i.
|
||||
// The result does not share its underlying array with list.
|
||||
func concat(list []int, i int) []int {
|
||||
var t []int
|
||||
t = append(t, list...)
|
||||
return append(t, i)
|
||||
}
|
||||
|
||||
// MissingMethod returns (nil, false) if typ implements T, otherwise
|
||||
|
|
@ -182,7 +182,7 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
|
|||
|
||||
if ityp, _ := typ.Underlying().(*Interface); ityp != nil {
|
||||
for _, m := range T.methods {
|
||||
obj := lookupMethod(ityp.methods, m.pkg, m.name)
|
||||
_, obj := lookupMethod(ityp.methods, m.pkg, m.name)
|
||||
if obj != nil && !IsIdentical(obj.Type(), m.typ) {
|
||||
return m, true
|
||||
}
|
||||
|
|
@ -192,7 +192,7 @@ func MissingMethod(typ Type, T *Interface) (method *Func, wrongType bool) {
|
|||
|
||||
// a concrete type implements T if it implements all methods of T.
|
||||
for _, m := range T.methods {
|
||||
obj, _, _ := LookupFieldOrMethod(typ, m.pkg, m.name)
|
||||
obj, _ := LookupFieldOrMethod(typ, m.pkg, m.name)
|
||||
if obj == nil {
|
||||
return m, false
|
||||
}
|
||||
|
|
@ -220,17 +220,17 @@ func fieldIndex(fields []*Field, pkg *Package, name string) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
// lookupMethod returns the method with matching package and name, or nil.
|
||||
func lookupMethod(methods []*Func, pkg *Package, name string) *Func {
|
||||
// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
|
||||
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
|
||||
assert(name != "_")
|
||||
for _, m := range methods {
|
||||
for i, m := range methods {
|
||||
// spec:
|
||||
// "Two identifiers are different if they are spelled differently,
|
||||
// or if they appear in different packages and are not exported.
|
||||
// Otherwise, they are the same."
|
||||
if m.name == name && (ast.IsExported(name) || m.pkg.path == pkg.path) {
|
||||
return m
|
||||
return i, m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return -1, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ type T1c struct {
|
|||
|
||||
func (T1c) Pointer /* ERROR "field and method" */ () int { return 0 }
|
||||
|
||||
var _ = T1c{}.Pointer
|
||||
var _ = T1c /* ERROR "no single field or method" */ {}.Pointer
|
||||
|
||||
// T2's method declared before the type.
|
||||
func (*T2) f /* ERROR "field and method" */ () {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue