1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // This file implements method sets.
15 // A MethodSet is an ordered set of concrete or abstract (interface) methods;
16 // a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id().
17 // The zero value for a MethodSet is a ready-to-use empty method set.
18 type MethodSet
struct {
22 func (s
*MethodSet
) String() string {
27 var buf strings
.Builder
28 fmt
.Fprintln(&buf
, "MethodSet {")
29 for _
, f
:= range s
.list
{
30 fmt
.Fprintf(&buf
, "\t%s\n", f
)
32 fmt
.Fprintln(&buf
, "}")
36 // Len returns the number of methods in s.
37 func (s
*MethodSet
) Len() int { return len(s
.list
) }
39 // At returns the i'th method in s for 0 <= i < s.Len().
40 func (s
*MethodSet
) At(i
int) *Selection
{ return s
.list
[i
] }
42 // Lookup returns the method with matching package and name, or nil if not found.
43 func (s
*MethodSet
) Lookup(pkg
*Package
, name
string) *Selection
{
49 i
:= sort
.Search(len(s
.list
), func(i
int) bool {
51 return m
.obj
.Id() >= key
55 if m
.obj
.Id() == key
{
62 // Shared empty method set.
63 var emptyMethodSet MethodSet
65 // Note: NewMethodSet is intended for external use only as it
66 // requires interfaces to be complete. It may be used
67 // internally if LookupFieldOrMethod completed the same
68 // interfaces beforehand.
70 // NewMethodSet returns the method set for the given type T.
71 // It always returns a non-nil method set, even if it is empty.
72 func NewMethodSet(T Type
) *MethodSet
{
73 // WARNING: The code in this function is extremely subtle - do not modify casually!
74 // This function and lookupFieldOrMethod should be kept in sync.
76 // TODO(rfindley) confirm that this code is in sync with lookupFieldOrMethod
77 // with respect to type params.
79 // method set up to the current depth, allocated lazily
82 typ
, isPtr
:= deref(T
)
84 // *typ where typ is an interface has no methods.
85 if isPtr
&& IsInterface(typ
) {
86 return &emptyMethodSet
89 // Start with typ as single entry at shallowest depth.
90 current
:= []embeddedType
{{typ
, nil, isPtr
, false}}
92 // Named types that we have seen already, allocated lazily.
93 // Used to avoid endless searches in case of recursive types.
94 // Since only Named types can be used for recursive types, we
95 // only need to track those.
96 // (If we ever allow type aliases to construct recursive types,
97 // we must use type identity rather than pointer equality for
98 // the map key comparison, as we do in consolidateMultiples.)
99 var seen
map[*Named
]bool
101 // collect methods at current depth
102 for len(current
) > 0 {
103 var next
[]embeddedType
// embedded types found at current depth
105 // field and method sets at current depth, indexed by names (Id's), and allocated lazily
106 var fset
map[string]bool // we only care about the field names
109 for _
, e
:= range current
{
112 // If we have a named type, we may have associated methods.
113 // Look for those first.
114 if named
, _
:= typ
.(*Named
); named
!= nil {
116 // We have seen this type before, at a more shallow depth
117 // (note that multiples of this type at the current depth
118 // were consolidated before). The type at that depth shadows
119 // this same type at the current depth, so we can ignore
124 seen
= make(map[*Named
]bool)
128 for i
:= 0; i
< named
.NumMethods(); i
++ {
129 mset
= mset
.addOne(named
.Method(i
), concat(e
.index
, i
), e
.indirect
, e
.multiples
)
133 switch t
:= under(typ
).(type) {
135 for i
, f
:= range t
.fields
{
137 fset
= make(map[string]bool)
141 // Embedded fields are always of the form T or *T where
142 // T is a type name. If typ appeared multiple times at
143 // this depth, f.Type appears multiple times at the next
146 typ
, isPtr
:= deref(f
.typ
)
147 // TODO(gri) optimization: ignore types that can't
148 // have fields or methods (only Named, Struct, and
149 // Interface types need to be considered).
150 next
= append(next
, embeddedType
{typ
, concat(e
.index
, i
), e
.indirect || isPtr
, e
.multiples
})
155 mset
= mset
.add(t
.typeSet().methods
, e
.index
, true, e
.multiples
)
159 // Add methods and collisions at this depth to base if no entries with matching
160 // names exist already.
161 for k
, m
:= range mset
{
162 if _
, found
:= base
[k
]; !found
{
163 // Fields collide with methods of the same name at this depth.
168 base
= make(methodSet
)
174 // Add all (remaining) fields at this depth as collisions (since they will
175 // hide any method further down) if no entries with matching names exist already.
176 for k
:= range fset
{
177 if _
, found
:= base
[k
]; !found
{
179 base
= make(methodSet
)
181 base
[k
] = nil // collision
185 current
= consolidateMultiples(next
)
189 return &emptyMethodSet
193 var list
[]*Selection
194 for _
, m
:= range base
{
197 list
= append(list
, m
)
200 // sort by unique name
201 sort
.Slice(list
, func(i
, j
int) bool {
202 return list
[i
].obj
.Id() < list
[j
].obj
.Id()
204 return &MethodSet
{list
}
207 // A methodSet is a set of methods and name collisions.
208 // A collision indicates that multiple methods with the
209 // same unique id, or a field with that id appeared.
210 type methodSet
map[string]*Selection
// a nil entry indicates a name collision
212 // Add adds all functions in list to the method set s.
213 // If multiples is set, every function in list appears multiple times
214 // and is treated as a collision.
215 func (s methodSet
) add(list
[]*Func
, index
[]int, indirect
bool, multiples
bool) methodSet
{
219 for i
, f
:= range list
{
220 s
= s
.addOne(f
, concat(index
, i
), indirect
, multiples
)
225 func (s methodSet
) addOne(f
*Func
, index
[]int, indirect
bool, multiples
bool) methodSet
{
230 // if f is not in the set, add it
232 // TODO(gri) A found method may not be added because it's not in the method set
233 // (!indirect && f.hasPtrRecv()). A 2nd method on the same level may be in the method
234 // set and may not collide with the first one, thus leading to a false positive.
235 // Is that possible? Investigate.
236 if _
, found
:= s
[key
]; !found
&& (indirect ||
!f
.hasPtrRecv()) {
237 s
[key
] = &Selection
{MethodVal
, nil, f
, index
, indirect
}
241 s
[key
] = nil // collision