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