GoPLS Viewer

Home|gopls/internal/apidiff/correspondence.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
5package apidiff
6
7import (
8    "go/types"
9    "sort"
10)
11
12// Two types are correspond if they are identical except for defined types,
13// which must correspond.
14//
15// Two defined types correspond if they can be interchanged in the old and new APIs,
16// possibly after a renaming.
17//
18// This is not a pure function. If we come across named types while traversing,
19// we establish correspondence.
20func (d *differcorrespond(oldnew types.Typebool {
21    return d.corr(oldnewnil)
22}
23
24// corr determines whether old and new correspond. The argument p is a list of
25// known interface identities, to avoid infinite recursion.
26//
27// corr calls itself recursively as much as possible, to establish more
28// correspondences and so check more of the API. E.g. if the new function has more
29// parameters than the old, compare all the old ones before returning false.
30//
31// Compare this to the implementation of go/types.Identical.
32func (d *differcorr(oldnew types.Typep *ifacePairbool {
33    // Structure copied from types.Identical.
34    switch old := old.(type) {
35    case *types.Basic:
36        return types.Identical(oldnew)
37
38    case *types.Array:
39        if newok := new.(*types.Array); ok {
40            return d.corr(old.Elem(), new.Elem(), p) && old.Len() == new.Len()
41        }
42
43    case *types.Slice:
44        if newok := new.(*types.Slice); ok {
45            return d.corr(old.Elem(), new.Elem(), p)
46        }
47
48    case *types.Map:
49        if newok := new.(*types.Map); ok {
50            return d.corr(old.Key(), new.Key(), p) && d.corr(old.Elem(), new.Elem(), p)
51        }
52
53    case *types.Chan:
54        if newok := new.(*types.Chan); ok {
55            return d.corr(old.Elem(), new.Elem(), p) && old.Dir() == new.Dir()
56        }
57
58    case *types.Pointer:
59        if newok := new.(*types.Pointer); ok {
60            return d.corr(old.Elem(), new.Elem(), p)
61        }
62
63    case *types.Signature:
64        if newok := new.(*types.Signature); ok {
65            pe := d.corr(old.Params(), new.Params(), p)
66            re := d.corr(old.Results(), new.Results(), p)
67            return old.Variadic() == new.Variadic() && pe && re
68        }
69
70    case *types.Tuple:
71        if newok := new.(*types.Tuple); ok {
72            for i := 0i < old.Len(); i++ {
73                if i >= new.Len() || !d.corr(old.At(i).Type(), new.At(i).Type(), p) {
74                    return false
75                }
76            }
77            return old.Len() == new.Len()
78        }
79
80    case *types.Struct:
81        if newok := new.(*types.Struct); ok {
82            for i := 0i < old.NumFields(); i++ {
83                if i >= new.NumFields() {
84                    return false
85                }
86                of := old.Field(i)
87                nf := new.Field(i)
88                if of.Anonymous() != nf.Anonymous() ||
89                    old.Tag(i) != new.Tag(i) ||
90                    !d.corr(of.Type(), nf.Type(), p) ||
91                    !d.corrFieldNames(ofnf) {
92                    return false
93                }
94            }
95            return old.NumFields() == new.NumFields()
96        }
97
98    case *types.Interface:
99        if newok := new.(*types.Interface); ok {
100            // Deal with circularity. See the comment in types.Identical.
101            q := &ifacePair{oldnewp}
102            for p != nil {
103                if p.identical(q) {
104                    return true // same pair was compared before
105                }
106                p = p.prev
107            }
108            oldms := d.sortedMethods(old)
109            newms := d.sortedMethods(new)
110            for iom := range oldms {
111                if i >= len(newms) {
112                    return false
113                }
114                nm := newms[i]
115                if d.methodID(om) != d.methodID(nm) || !d.corr(om.Type(), nm.Type(), q) {
116                    return false
117                }
118            }
119            return old.NumMethods() == new.NumMethods()
120        }
121
122    case *types.Named:
123        if newok := new.(*types.Named); ok {
124            return d.establishCorrespondence(oldnew)
125        }
126        if newok := new.(*types.Basic); ok {
127            // Basic types are defined types, too, so we have to support them.
128
129            return d.establishCorrespondence(oldnew)
130        }
131
132    default:
133        panic("unknown type kind")
134    }
135    return false
136}
137
138// Compare old and new field names. We are determining correspondence across packages,
139// so just compare names, not packages. For an unexported, embedded field of named
140// type (non-named embedded fields are possible with aliases), we check that the type
141// names correspond. We check the types for correspondence before this is called, so
142// we've established correspondence.
143func (d *differcorrFieldNames(ofnf *types.Varbool {
144    if of.Anonymous() && nf.Anonymous() && !of.Exported() && !nf.Exported() {
145        if onok := of.Type().(*types.Named); ok {
146            nn := nf.Type().(*types.Named)
147            return d.establishCorrespondence(onnn)
148        }
149    }
150    return of.Name() == nf.Name()
151}
152
153// Establish that old corresponds with new if it does not already
154// correspond to something else.
155func (d *differestablishCorrespondence(old *types.Namednew types.Typebool {
156    oldname := old.Obj()
157    oldc := d.correspondMap[oldname]
158    if oldc == nil {
159        // For now, assume the types don't correspond unless they are from the old
160        // and new packages, respectively.
161        //
162        // This is too conservative. For instance,
163        //    [old] type A = q.B; [new] type A q.C
164        // could be OK if in package q, B is an alias for C.
165        // Or, using p as the name of the current old/new packages:
166        //    [old] type A = q.B; [new] type A int
167        // could be OK if in q,
168        //    [old] type B int; [new] type B = p.A
169        // In this case, p.A and q.B name the same type in both old and new worlds.
170        // Note that this case doesn't imply circular package imports: it's possible
171        // that in the old world, p imports q, but in the new, q imports p.
172        //
173        // However, if we didn't do something here, then we'd incorrectly allow cases
174        // like the first one above in which q.B is not an alias for q.C
175        //
176        // What we should do is check that the old type, in the new world's package
177        // of the same path, doesn't correspond to something other than the new type.
178        // That is a bit hard, because there is no easy way to find a new package
179        // matching an old one.
180        if newnok := new.(*types.Named); ok {
181            if old.Obj().Pkg() != d.old || newn.Obj().Pkg() != d.new {
182                return old.Obj().Id() == newn.Obj().Id()
183            }
184        }
185        // If there is no correspondence, create one.
186        d.correspondMap[oldname] = new
187        // Check that the corresponding types are compatible.
188        d.checkCompatibleDefined(oldnameoldnew)
189        return true
190    }
191    return types.Identical(oldcnew)
192}
193
194func (d *differsortedMethods(iface *types.Interface) []*types.Func {
195    ms := make([]*types.Funciface.NumMethods())
196    for i := 0i < iface.NumMethods(); i++ {
197        ms[i] = iface.Method(i)
198    }
199    sort.Slice(ms, func(ij intbool { return d.methodID(ms[i]) < d.methodID(ms[j]) })
200    return ms
201}
202
203func (d *differmethodID(m *types.Funcstring {
204    // If the method belongs to one of the two packages being compared, use
205    // just its name even if it's unexported. That lets us treat unexported names
206    // from the old and new packages as equal.
207    if m.Pkg() == d.old || m.Pkg() == d.new {
208        return m.Name()
209    }
210    return m.Id()
211}
212
213// Copied from the go/types package:
214
215// An ifacePair is a node in a stack of interface type pairs compared for identity.
216type ifacePair struct {
217    xy *types.Interface
218    prev *ifacePair
219}
220
221func (p *ifacePairidentical(q *ifacePairbool {
222    return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
223}
224
MembersX
differ.establishCorrespondence
differ.sortedMethods
differ.sortedMethods.iface
differ.corr.BlockStmt.BlockStmt.pe
differ.corr.BlockStmt.BlockStmt.re
differ.corr.BlockStmt.BlockStmt.i
differ.corr.BlockStmt.BlockStmt.RangeStmt_3148.i
differ.corrFieldNames.d
differ.sortedMethods.i
ifacePair.y
ifacePair.prev
ifacePair.identical.p
differ.corr.d
differ.corr.old
differ.corr.BlockStmt.BlockStmt.BlockStmt.nf
differ.methodID.m
ifacePair.x
differ.correspond
differ.establishCorrespondence.d
ifacePair.identical.q
differ.correspond.new
differ.corr.BlockStmt.BlockStmt.BlockStmt.of
differ.methodID.d
differ.methodID
differ.corr.BlockStmt.BlockStmt.q
differ.corr.BlockStmt.BlockStmt.oldms
differ.corrFieldNames.of
differ.corrFieldNames.nf
ifacePair
differ.corr.BlockStmt.BlockStmt.newms
differ.corrFieldNames
differ.establishCorrespondence.new
differ.sortedMethods.d
ifacePair.identical
differ.sortedMethods.ms
differ.correspond.old
differ.corr.p
differ.corr.BlockStmt.BlockStmt.RangeStmt_3148.om
differ.establishCorrespondence.old
differ.establishCorrespondence.oldname
differ.correspond.d
differ.corr
differ.corr.new
Members
X