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