Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / apidiff / compatibility.go
1 package apidiff
2
3 import (
4         "fmt"
5         "go/types"
6         "reflect"
7 )
8
9 func (d *differ) checkCompatible(otn *types.TypeName, old, new types.Type) {
10         switch old := old.(type) {
11         case *types.Interface:
12                 if new, ok := new.(*types.Interface); ok {
13                         d.checkCompatibleInterface(otn, old, new)
14                         return
15                 }
16
17         case *types.Struct:
18                 if new, ok := new.(*types.Struct); ok {
19                         d.checkCompatibleStruct(otn, old, new)
20                         return
21                 }
22
23         case *types.Chan:
24                 if new, ok := new.(*types.Chan); ok {
25                         d.checkCompatibleChan(otn, old, new)
26                         return
27                 }
28
29         case *types.Basic:
30                 if new, ok := new.(*types.Basic); ok {
31                         d.checkCompatibleBasic(otn, old, new)
32                         return
33                 }
34
35         case *types.Named:
36                 panic("unreachable")
37
38         default:
39                 d.checkCorrespondence(otn, "", old, new)
40                 return
41
42         }
43         // Here if old and new are different kinds of types.
44         d.typeChanged(otn, "", old, new)
45 }
46
47 func (d *differ) checkCompatibleChan(otn *types.TypeName, old, new *types.Chan) {
48         d.checkCorrespondence(otn, ", element type", old.Elem(), new.Elem())
49         if old.Dir() != new.Dir() {
50                 if new.Dir() == types.SendRecv {
51                         d.compatible(otn, "", "removed direction")
52                 } else {
53                         d.incompatible(otn, "", "changed direction")
54                 }
55         }
56 }
57
58 func (d *differ) checkCompatibleBasic(otn *types.TypeName, old, new *types.Basic) {
59         // Certain changes to numeric types are compatible. Approximately, the info must
60         // be the same, and the new values must be a superset of the old.
61         if old.Kind() == new.Kind() {
62                 // old and new are identical
63                 return
64         }
65         if compatibleBasics[[2]types.BasicKind{old.Kind(), new.Kind()}] {
66                 d.compatible(otn, "", "changed from %s to %s", old, new)
67         } else {
68                 d.typeChanged(otn, "", old, new)
69         }
70 }
71
72 // All pairs (old, new) of compatible basic types.
73 var compatibleBasics = map[[2]types.BasicKind]bool{
74         {types.Uint8, types.Uint16}:         true,
75         {types.Uint8, types.Uint32}:         true,
76         {types.Uint8, types.Uint}:           true,
77         {types.Uint8, types.Uint64}:         true,
78         {types.Uint16, types.Uint32}:        true,
79         {types.Uint16, types.Uint}:          true,
80         {types.Uint16, types.Uint64}:        true,
81         {types.Uint32, types.Uint}:          true,
82         {types.Uint32, types.Uint64}:        true,
83         {types.Uint, types.Uint64}:          true,
84         {types.Int8, types.Int16}:           true,
85         {types.Int8, types.Int32}:           true,
86         {types.Int8, types.Int}:             true,
87         {types.Int8, types.Int64}:           true,
88         {types.Int16, types.Int32}:          true,
89         {types.Int16, types.Int}:            true,
90         {types.Int16, types.Int64}:          true,
91         {types.Int32, types.Int}:            true,
92         {types.Int32, types.Int64}:          true,
93         {types.Int, types.Int64}:            true,
94         {types.Float32, types.Float64}:      true,
95         {types.Complex64, types.Complex128}: true,
96 }
97
98 // Interface compatibility:
99 // If the old interface has an unexported method, the new interface is compatible
100 // if its exported method set is a superset of the old. (Users could not implement,
101 // only embed.)
102 //
103 // If the old interface did not have an unexported method, the new interface is
104 // compatible if its exported method set is the same as the old, and it has no
105 // unexported methods. (Adding an unexported method makes the interface
106 // unimplementable outside the package.)
107 //
108 // TODO: must also check that if any methods were added or removed, every exposed
109 // type in the package that implemented the interface in old still implements it in
110 // new. Otherwise external assignments could fail.
111 func (d *differ) checkCompatibleInterface(otn *types.TypeName, old, new *types.Interface) {
112         // Method sets are checked in checkCompatibleDefined.
113
114         // Does the old interface have an unexported method?
115         if unexportedMethod(old) != nil {
116                 d.checkMethodSet(otn, old, new, additionsCompatible)
117         } else {
118                 // Perform an equivalence check, but with more information.
119                 d.checkMethodSet(otn, old, new, additionsIncompatible)
120                 if u := unexportedMethod(new); u != nil {
121                         d.incompatible(otn, u.Name(), "added unexported method")
122                 }
123         }
124 }
125
126 // Return an unexported method from the method set of t, or nil if there are none.
127 func unexportedMethod(t *types.Interface) *types.Func {
128         for i := 0; i < t.NumMethods(); i++ {
129                 if m := t.Method(i); !m.Exported() {
130                         return m
131                 }
132         }
133         return nil
134 }
135
136 // We need to check three things for structs:
137 // 1. The set of exported fields must be compatible. This ensures that keyed struct
138 //    literals continue to compile. (There is no compatibility guarantee for unkeyed
139 //    struct literals.)
140 // 2. The set of exported *selectable* fields must be compatible. This includes the exported
141 //    fields of all embedded structs. This ensures that selections continue to compile.
142 // 3. If the old struct is comparable, so must the new one be. This ensures that equality
143 //    expressions and uses of struct values as map keys continue to compile.
144 //
145 // An unexported embedded struct can't appear in a struct literal outside the
146 // package, so it doesn't have to be present, or have the same name, in the new
147 // struct.
148 //
149 // Field tags are ignored: they have no compile-time implications.
150 func (d *differ) checkCompatibleStruct(obj types.Object, old, new *types.Struct) {
151         d.checkCompatibleObjectSets(obj, exportedFields(old), exportedFields(new))
152         d.checkCompatibleObjectSets(obj, exportedSelectableFields(old), exportedSelectableFields(new))
153         // Removing comparability from a struct is an incompatible change.
154         if types.Comparable(old) && !types.Comparable(new) {
155                 d.incompatible(obj, "", "old is comparable, new is not")
156         }
157 }
158
159 // exportedFields collects all the immediate fields of the struct that are exported.
160 // This is also the set of exported keys for keyed struct literals.
161 func exportedFields(s *types.Struct) map[string]types.Object {
162         m := map[string]types.Object{}
163         for i := 0; i < s.NumFields(); i++ {
164                 f := s.Field(i)
165                 if f.Exported() {
166                         m[f.Name()] = f
167                 }
168         }
169         return m
170 }
171
172 // exportedSelectableFields collects all the exported fields of the struct, including
173 // exported fields of embedded structs.
174 //
175 // We traverse the struct breadth-first, because of the rule that a lower-depth field
176 // shadows one at a higher depth.
177 func exportedSelectableFields(s *types.Struct) map[string]types.Object {
178         var (
179                 m    = map[string]types.Object{}
180                 next []*types.Struct // embedded structs at the next depth
181                 seen []*types.Struct // to handle recursive embedding
182         )
183         for cur := []*types.Struct{s}; len(cur) > 0; cur, next = next, nil {
184                 seen = append(seen, cur...)
185                 // We only want to consider unambiguous fields. Ambiguous fields (where there
186                 // is more than one field of the same name at the same level) are legal, but
187                 // cannot be selected.
188                 for name, f := range unambiguousFields(cur) {
189                         // Record an exported field we haven't seen before. If we have seen it,
190                         // it occurred a lower depth, so it shadows this field.
191                         if f.Exported() && m[name] == nil {
192                                 m[name] = f
193                         }
194                         // Remember embedded structs for processing at the next depth,
195                         // but only if we haven't seen the struct at this depth or above.
196                         if !f.Anonymous() {
197                                 continue
198                         }
199                         t := f.Type().Underlying()
200                         if p, ok := t.(*types.Pointer); ok {
201                                 t = p.Elem().Underlying()
202                         }
203                         if t, ok := t.(*types.Struct); ok && !contains(seen, t) {
204                                 next = append(next, t)
205                         }
206                 }
207         }
208         return m
209 }
210
211 func contains(ts []*types.Struct, t *types.Struct) bool {
212         for _, s := range ts {
213                 if types.Identical(s, t) {
214                         return true
215                 }
216         }
217         return false
218 }
219
220 // Given a set of structs at the same depth, the unambiguous fields are the ones whose
221 // names appear exactly once.
222 func unambiguousFields(structs []*types.Struct) map[string]*types.Var {
223         fields := map[string]*types.Var{}
224         seen := map[string]bool{}
225         for _, s := range structs {
226                 for i := 0; i < s.NumFields(); i++ {
227                         f := s.Field(i)
228                         name := f.Name()
229                         if seen[name] {
230                                 delete(fields, name)
231                         } else {
232                                 seen[name] = true
233                                 fields[name] = f
234                         }
235                 }
236         }
237         return fields
238 }
239
240 // Anything removed or change from the old set is an incompatible change.
241 // Anything added to the new set is a compatible change.
242 func (d *differ) checkCompatibleObjectSets(obj types.Object, old, new map[string]types.Object) {
243         for name, oldo := range old {
244                 newo := new[name]
245                 if newo == nil {
246                         d.incompatible(obj, name, "removed")
247                 } else {
248                         d.checkCorrespondence(obj, name, oldo.Type(), newo.Type())
249                 }
250         }
251         for name := range new {
252                 if old[name] == nil {
253                         d.compatible(obj, name, "added")
254                 }
255         }
256 }
257
258 func (d *differ) checkCompatibleDefined(otn *types.TypeName, old *types.Named, new types.Type) {
259         // We've already checked that old and new correspond.
260         d.checkCompatible(otn, old.Underlying(), new.Underlying())
261         // If there are different kinds of types (e.g. struct and interface), don't bother checking
262         // the method sets.
263         if reflect.TypeOf(old.Underlying()) != reflect.TypeOf(new.Underlying()) {
264                 return
265         }
266         // Interface method sets are checked in checkCompatibleInterface.
267         if _, ok := old.Underlying().(*types.Interface); ok {
268                 return
269         }
270
271         // A new method set is compatible with an old if the new exported methods are a superset of the old.
272         d.checkMethodSet(otn, old, new, additionsCompatible)
273         d.checkMethodSet(otn, types.NewPointer(old), types.NewPointer(new), additionsCompatible)
274 }
275
276 const (
277         additionsCompatible   = true
278         additionsIncompatible = false
279 )
280
281 func (d *differ) checkMethodSet(otn *types.TypeName, oldt, newt types.Type, addcompat bool) {
282         // TODO: find a way to use checkCompatibleObjectSets for this.
283         oldMethodSet := exportedMethods(oldt)
284         newMethodSet := exportedMethods(newt)
285         msname := otn.Name()
286         if _, ok := oldt.(*types.Pointer); ok {
287                 msname = "*" + msname
288         }
289         for name, oldMethod := range oldMethodSet {
290                 newMethod := newMethodSet[name]
291                 if newMethod == nil {
292                         var part string
293                         // Due to embedding, it's possible that the method's receiver type is not
294                         // the same as the defined type whose method set we're looking at. So for
295                         // a type T with removed method M that is embedded in some other type U,
296                         // we will generate two "removed" messages for T.M, one for its own type
297                         // T and one for the embedded type U. We want both messages to appear,
298                         // but the messageSet dedup logic will allow only one message for a given
299                         // object. So use the part string to distinguish them.
300                         if receiverNamedType(oldMethod).Obj() != otn {
301                                 part = fmt.Sprintf(", method set of %s", msname)
302                         }
303                         d.incompatible(oldMethod, part, "removed")
304                 } else {
305                         obj := oldMethod
306                         // If a value method is changed to a pointer method and has a signature
307                         // change, then we can get two messages for the same method definition: one
308                         // for the value method set that says it's removed, and another for the
309                         // pointer method set that says it changed. To keep both messages (since
310                         // messageSet dedups), use newMethod for the second. (Slight hack.)
311                         if !hasPointerReceiver(oldMethod) && hasPointerReceiver(newMethod) {
312                                 obj = newMethod
313                         }
314                         d.checkCorrespondence(obj, "", oldMethod.Type(), newMethod.Type())
315                 }
316         }
317
318         // Check for added methods.
319         for name, newMethod := range newMethodSet {
320                 if oldMethodSet[name] == nil {
321                         if addcompat {
322                                 d.compatible(newMethod, "", "added")
323                         } else {
324                                 d.incompatible(newMethod, "", "added")
325                         }
326                 }
327         }
328 }
329
330 // exportedMethods collects all the exported methods of type's method set.
331 func exportedMethods(t types.Type) map[string]types.Object {
332         m := map[string]types.Object{}
333         ms := types.NewMethodSet(t)
334         for i := 0; i < ms.Len(); i++ {
335                 obj := ms.At(i).Obj()
336                 if obj.Exported() {
337                         m[obj.Name()] = obj
338                 }
339         }
340         return m
341 }
342
343 func receiverType(method types.Object) types.Type {
344         return method.Type().(*types.Signature).Recv().Type()
345 }
346
347 func receiverNamedType(method types.Object) *types.Named {
348         switch t := receiverType(method).(type) {
349         case *types.Pointer:
350                 return t.Elem().(*types.Named)
351         case *types.Named:
352                 return t
353         default:
354                 panic("unreachable")
355         }
356 }
357
358 func hasPointerReceiver(method types.Object) bool {
359         _, ok := receiverType(method).(*types.Pointer)
360         return ok
361 }