github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/godoc/analysis/implements14.go (about) 1 // Copyright 2014 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 // +build !go1.5 6 7 package analysis 8 9 // This file computes the "implements" relation over all pairs of 10 // named types in the program. (The mark-up is done by typeinfo.go.) 11 12 // TODO(adonovan): do we want to report implements(C, I) where C and I 13 // belong to different packages and at least one is not exported? 14 15 import ( 16 "sort" 17 18 "golang.org/x/tools/go/types" 19 "golang.org/x/tools/go/types/typeutil" 20 ) 21 22 // computeImplements computes the "implements" relation over all pairs 23 // of named types in allNamed. 24 func computeImplements(cache *typeutil.MethodSetCache, allNamed []*types.Named) map[*types.Named]implementsFacts { 25 // Information about a single type's method set. 26 type msetInfo struct { 27 typ types.Type 28 mset *types.MethodSet 29 mask1, mask2 uint64 30 } 31 32 initMsetInfo := func(info *msetInfo, typ types.Type) { 33 info.typ = typ 34 info.mset = cache.MethodSet(typ) 35 for i := 0; i < info.mset.Len(); i++ { 36 name := info.mset.At(i).Obj().Name() 37 info.mask1 |= 1 << methodBit(name[0]) 38 info.mask2 |= 1 << methodBit(name[len(name)-1]) 39 } 40 } 41 42 // satisfies(T, U) reports whether type T satisfies type U. 43 // U must be an interface. 44 // 45 // Since there are thousands of types (and thus millions of 46 // pairs of types) and types.Assignable(T, U) is relatively 47 // expensive, we compute assignability directly from the 48 // method sets. (At least one of T and U must be an 49 // interface.) 50 // 51 // We use a trick (thanks gri!) related to a Bloom filter to 52 // quickly reject most tests, which are false. For each 53 // method set, we precompute a mask, a set of bits, one per 54 // distinct initial byte of each method name. Thus the mask 55 // for io.ReadWriter would be {'R','W'}. AssignableTo(T, U) 56 // cannot be true unless mask(T)&mask(U)==mask(U). 57 // 58 // As with a Bloom filter, we can improve precision by testing 59 // additional hashes, e.g. using the last letter of each 60 // method name, so long as the subset mask property holds. 61 // 62 // When analyzing the standard library, there are about 1e6 63 // calls to satisfies(), of which 0.6% return true. With a 64 // 1-hash filter, 95% of calls avoid the expensive check; with 65 // a 2-hash filter, this grows to 98.2%. 66 satisfies := func(T, U *msetInfo) bool { 67 return T.mask1&U.mask1 == U.mask1 && 68 T.mask2&U.mask2 == U.mask2 && 69 containsAllIdsOf(T.mset, U.mset) 70 } 71 72 // Information about a named type N, and perhaps also *N. 73 type namedInfo struct { 74 isInterface bool 75 base msetInfo // N 76 ptr msetInfo // *N, iff N !isInterface 77 } 78 79 var infos []namedInfo 80 81 // Precompute the method sets and their masks. 82 for _, N := range allNamed { 83 var info namedInfo 84 initMsetInfo(&info.base, N) 85 _, info.isInterface = N.Underlying().(*types.Interface) 86 if !info.isInterface { 87 initMsetInfo(&info.ptr, types.NewPointer(N)) 88 } 89 90 if info.base.mask1|info.ptr.mask1 == 0 { 91 continue // neither N nor *N has methods 92 } 93 94 infos = append(infos, info) 95 } 96 97 facts := make(map[*types.Named]implementsFacts) 98 99 // Test all pairs of distinct named types (T, U). 100 // TODO(adonovan): opt: compute (U, T) at the same time. 101 for t := range infos { 102 T := &infos[t] 103 var to, from, fromPtr []types.Type 104 for u := range infos { 105 if t == u { 106 continue 107 } 108 U := &infos[u] 109 switch { 110 case T.isInterface && U.isInterface: 111 if satisfies(&U.base, &T.base) { 112 to = append(to, U.base.typ) 113 } 114 if satisfies(&T.base, &U.base) { 115 from = append(from, U.base.typ) 116 } 117 case T.isInterface: // U concrete 118 if satisfies(&U.base, &T.base) { 119 to = append(to, U.base.typ) 120 } else if satisfies(&U.ptr, &T.base) { 121 to = append(to, U.ptr.typ) 122 } 123 case U.isInterface: // T concrete 124 if satisfies(&T.base, &U.base) { 125 from = append(from, U.base.typ) 126 } else if satisfies(&T.ptr, &U.base) { 127 fromPtr = append(fromPtr, U.base.typ) 128 } 129 } 130 } 131 132 // Sort types (arbitrarily) to avoid nondeterminism. 133 sort.Sort(typesByString(to)) 134 sort.Sort(typesByString(from)) 135 sort.Sort(typesByString(fromPtr)) 136 137 facts[T.base.typ.(*types.Named)] = implementsFacts{to, from, fromPtr} 138 } 139 140 return facts 141 } 142 143 type implementsFacts struct { 144 to []types.Type // named or ptr-to-named types assignable to interface T 145 from []types.Type // named interfaces assignable from T 146 fromPtr []types.Type // named interfaces assignable only from *T 147 } 148 149 type typesByString []types.Type 150 151 func (p typesByString) Len() int { return len(p) } 152 func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() } 153 func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 154 155 // methodBit returns the index of x in [a-zA-Z], or 52 if not found. 156 func methodBit(x byte) uint64 { 157 switch { 158 case 'a' <= x && x <= 'z': 159 return uint64(x - 'a') 160 case 'A' <= x && x <= 'Z': 161 return uint64(26 + x - 'A') 162 } 163 return 52 // all other bytes 164 } 165 166 // containsAllIdsOf reports whether the method identifiers of T are a 167 // superset of those in U. If U belongs to an interface type, the 168 // result is equal to types.Assignable(T, U), but is cheaper to compute. 169 // 170 // TODO(gri): make this a method of *types.MethodSet. 171 // 172 func containsAllIdsOf(T, U *types.MethodSet) bool { 173 t, tlen := 0, T.Len() 174 u, ulen := 0, U.Len() 175 for t < tlen && u < ulen { 176 tMeth := T.At(t).Obj() 177 uMeth := U.At(u).Obj() 178 tId := tMeth.Id() 179 uId := uMeth.Id() 180 if tId > uId { 181 // U has a method T lacks: fail. 182 return false 183 } 184 if tId < uId { 185 // T has a method U lacks: ignore it. 186 t++ 187 continue 188 } 189 // U and T both have a method of this Id. Check types. 190 if !types.Identical(tMeth.Type(), uMeth.Type()) { 191 return false // type mismatch 192 } 193 u++ 194 t++ 195 } 196 return u == ulen 197 }