Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / apidiff / apidiff.go
1 // TODO: test swap corresponding types (e.g. u1 <-> u2 and u2 <-> u1)
2 // TODO: test exported alias refers to something in another package -- does correspondence work then?
3 // TODO: CODE COVERAGE
4 // TODO: note that we may miss correspondences because we bail early when we compare a signature (e.g. when lengths differ; we could do up to the shorter)
5 // TODO: if you add an unexported method to an exposed interface, you have to check that
6 //              every exposed type that previously implemented the interface still does. Otherwise
7 //              an external assignment of the exposed type to the interface type could fail.
8 // TODO: check constant values: large values aren't representable by some types.
9 // TODO: Document all the incompatibilities we don't check for.
10
11 package apidiff
12
13 import (
14         "fmt"
15         "go/constant"
16         "go/token"
17         "go/types"
18 )
19
20 // Changes reports on the differences between the APIs of the old and new packages.
21 // It classifies each difference as either compatible or incompatible (breaking.) For
22 // a detailed discussion of what constitutes an incompatible change, see the package
23 // documentation.
24 func Changes(old, new *types.Package) Report {
25         d := newDiffer(old, new)
26         d.checkPackage()
27         r := Report{}
28         for _, m := range d.incompatibles.collect() {
29                 r.Changes = append(r.Changes, Change{Message: m, Compatible: false})
30         }
31         for _, m := range d.compatibles.collect() {
32                 r.Changes = append(r.Changes, Change{Message: m, Compatible: true})
33         }
34         return r
35 }
36
37 type differ struct {
38         old, new *types.Package
39         // Correspondences between named types.
40         // Even though it is the named types (*types.Named) that correspond, we use
41         // *types.TypeName as a map key because they are canonical.
42         // The values can be either named types or basic types.
43         correspondMap map[*types.TypeName]types.Type
44
45         // Messages.
46         incompatibles messageSet
47         compatibles   messageSet
48 }
49
50 func newDiffer(old, new *types.Package) *differ {
51         return &differ{
52                 old:           old,
53                 new:           new,
54                 correspondMap: map[*types.TypeName]types.Type{},
55                 incompatibles: messageSet{},
56                 compatibles:   messageSet{},
57         }
58 }
59
60 func (d *differ) incompatible(obj types.Object, part, format string, args ...interface{}) {
61         addMessage(d.incompatibles, obj, part, format, args)
62 }
63
64 func (d *differ) compatible(obj types.Object, part, format string, args ...interface{}) {
65         addMessage(d.compatibles, obj, part, format, args)
66 }
67
68 func addMessage(ms messageSet, obj types.Object, part, format string, args []interface{}) {
69         ms.add(obj, part, fmt.Sprintf(format, args...))
70 }
71
72 func (d *differ) checkPackage() {
73         // Old changes.
74         for _, name := range d.old.Scope().Names() {
75                 oldobj := d.old.Scope().Lookup(name)
76                 if !oldobj.Exported() {
77                         continue
78                 }
79                 newobj := d.new.Scope().Lookup(name)
80                 if newobj == nil {
81                         d.incompatible(oldobj, "", "removed")
82                         continue
83                 }
84                 d.checkObjects(oldobj, newobj)
85         }
86         // New additions.
87         for _, name := range d.new.Scope().Names() {
88                 newobj := d.new.Scope().Lookup(name)
89                 if newobj.Exported() && d.old.Scope().Lookup(name) == nil {
90                         d.compatible(newobj, "", "added")
91                 }
92         }
93
94         // Whole-package satisfaction.
95         // For every old exposed interface oIface and its corresponding new interface nIface...
96         for otn1, nt1 := range d.correspondMap {
97                 oIface, ok := otn1.Type().Underlying().(*types.Interface)
98                 if !ok {
99                         continue
100                 }
101                 nIface, ok := nt1.Underlying().(*types.Interface)
102                 if !ok {
103                         // If nt1 isn't an interface but otn1 is, then that's an incompatibility that
104                         // we've already noticed, so there's no need to do anything here.
105                         continue
106                 }
107                 // For every old type that implements oIface, its corresponding new type must implement
108                 // nIface.
109                 for otn2, nt2 := range d.correspondMap {
110                         if otn1 == otn2 {
111                                 continue
112                         }
113                         if types.Implements(otn2.Type(), oIface) && !types.Implements(nt2, nIface) {
114                                 d.incompatible(otn2, "", "no longer implements %s", objectString(otn1))
115                         }
116                 }
117         }
118 }
119
120 func (d *differ) checkObjects(old, new types.Object) {
121         switch old := old.(type) {
122         case *types.Const:
123                 if new, ok := new.(*types.Const); ok {
124                         d.constChanges(old, new)
125                         return
126                 }
127         case *types.Var:
128                 if new, ok := new.(*types.Var); ok {
129                         d.checkCorrespondence(old, "", old.Type(), new.Type())
130                         return
131                 }
132         case *types.Func:
133                 switch new := new.(type) {
134                 case *types.Func:
135                         d.checkCorrespondence(old, "", old.Type(), new.Type())
136                         return
137                 case *types.Var:
138                         d.compatible(old, "", "changed from func to var")
139                         d.checkCorrespondence(old, "", old.Type(), new.Type())
140                         return
141
142                 }
143         case *types.TypeName:
144                 if new, ok := new.(*types.TypeName); ok {
145                         d.checkCorrespondence(old, "", old.Type(), new.Type())
146                         return
147                 }
148         default:
149                 panic("unexpected obj type")
150         }
151         // Here if kind of type changed.
152         d.incompatible(old, "", "changed from %s to %s",
153                 objectKindString(old), objectKindString(new))
154 }
155
156 // Compare two constants.
157 func (d *differ) constChanges(old, new *types.Const) {
158         ot := old.Type()
159         nt := new.Type()
160         // Check for change of type.
161         if !d.correspond(ot, nt) {
162                 d.typeChanged(old, "", ot, nt)
163                 return
164         }
165         // Check for change of value.
166         // We know the types are the same, so constant.Compare shouldn't panic.
167         if !constant.Compare(old.Val(), token.EQL, new.Val()) {
168                 d.incompatible(old, "", "value changed from %s to %s", old.Val(), new.Val())
169         }
170 }
171
172 func objectKindString(obj types.Object) string {
173         switch obj.(type) {
174         case *types.Const:
175                 return "const"
176         case *types.Var:
177                 return "var"
178         case *types.Func:
179                 return "func"
180         case *types.TypeName:
181                 return "type"
182         default:
183                 return "???"
184         }
185 }
186
187 func (d *differ) checkCorrespondence(obj types.Object, part string, old, new types.Type) {
188         if !d.correspond(old, new) {
189                 d.typeChanged(obj, part, old, new)
190         }
191 }
192
193 func (d *differ) typeChanged(obj types.Object, part string, old, new types.Type) {
194         old = removeNamesFromSignature(old)
195         new = removeNamesFromSignature(new)
196         olds := types.TypeString(old, types.RelativeTo(d.old))
197         news := types.TypeString(new, types.RelativeTo(d.new))
198         d.incompatible(obj, part, "changed from %s to %s", olds, news)
199 }
200
201 // go/types always includes the argument and result names when formatting a signature.
202 // Since these can change without affecting compatibility, we don't want users to
203 // be distracted by them, so we remove them.
204 func removeNamesFromSignature(t types.Type) types.Type {
205         sig, ok := t.(*types.Signature)
206         if !ok {
207                 return t
208         }
209
210         dename := func(p *types.Tuple) *types.Tuple {
211                 var vars []*types.Var
212                 for i := 0; i < p.Len(); i++ {
213                         v := p.At(i)
214                         vars = append(vars, types.NewVar(v.Pos(), v.Pkg(), "", v.Type()))
215                 }
216                 return types.NewTuple(vars...)
217         }
218
219         return types.NewSignature(sig.Recv(), dename(sig.Params()), dename(sig.Results()), sig.Variadic())
220 }