You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
5.7 KiB

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