--- /dev/null
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package analysis
+
+// This file computes the "implements" relation over all pairs of
+// named types in the program. (The mark-up is done by typeinfo.go.)
+
+// TODO(adonovan): do we want to report implements(C, I) where C and I
+// belong to different packages and at least one is not exported?
+
+import (
+ "go/types"
+ "sort"
+
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// computeImplements computes the "implements" relation over all pairs
+// of named types in allNamed.
+func computeImplements(cache *typeutil.MethodSetCache, allNamed []*types.Named) map[*types.Named]implementsFacts {
+ // Information about a single type's method set.
+ type msetInfo struct {
+ typ types.Type
+ mset *types.MethodSet
+ mask1, mask2 uint64
+ }
+
+ initMsetInfo := func(info *msetInfo, typ types.Type) {
+ info.typ = typ
+ info.mset = cache.MethodSet(typ)
+ for i := 0; i < info.mset.Len(); i++ {
+ name := info.mset.At(i).Obj().Name()
+ info.mask1 |= 1 << methodBit(name[0])
+ info.mask2 |= 1 << methodBit(name[len(name)-1])
+ }
+ }
+
+ // satisfies(T, U) reports whether type T satisfies type U.
+ // U must be an interface.
+ //
+ // Since there are thousands of types (and thus millions of
+ // pairs of types) and types.Assignable(T, U) is relatively
+ // expensive, we compute assignability directly from the
+ // method sets. (At least one of T and U must be an
+ // interface.)
+ //
+ // We use a trick (thanks gri!) related to a Bloom filter to
+ // quickly reject most tests, which are false. For each
+ // method set, we precompute a mask, a set of bits, one per
+ // distinct initial byte of each method name. Thus the mask
+ // for io.ReadWriter would be {'R','W'}. AssignableTo(T, U)
+ // cannot be true unless mask(T)&mask(U)==mask(U).
+ //
+ // As with a Bloom filter, we can improve precision by testing
+ // additional hashes, e.g. using the last letter of each
+ // method name, so long as the subset mask property holds.
+ //
+ // When analyzing the standard library, there are about 1e6
+ // calls to satisfies(), of which 0.6% return true. With a
+ // 1-hash filter, 95% of calls avoid the expensive check; with
+ // a 2-hash filter, this grows to 98.2%.
+ satisfies := func(T, U *msetInfo) bool {
+ return T.mask1&U.mask1 == U.mask1 &&
+ T.mask2&U.mask2 == U.mask2 &&
+ containsAllIdsOf(T.mset, U.mset)
+ }
+
+ // Information about a named type N, and perhaps also *N.
+ type namedInfo struct {
+ isInterface bool
+ base msetInfo // N
+ ptr msetInfo // *N, iff N !isInterface
+ }
+
+ var infos []namedInfo
+
+ // Precompute the method sets and their masks.
+ for _, N := range allNamed {
+ var info namedInfo
+ initMsetInfo(&info.base, N)
+ _, info.isInterface = N.Underlying().(*types.Interface)
+ if !info.isInterface {
+ initMsetInfo(&info.ptr, types.NewPointer(N))
+ }
+
+ if info.base.mask1|info.ptr.mask1 == 0 {
+ continue // neither N nor *N has methods
+ }
+
+ infos = append(infos, info)
+ }
+
+ facts := make(map[*types.Named]implementsFacts)
+
+ // Test all pairs of distinct named types (T, U).
+ // TODO(adonovan): opt: compute (U, T) at the same time.
+ for t := range infos {
+ T := &infos[t]
+ var to, from, fromPtr []types.Type
+ for u := range infos {
+ if t == u {
+ continue
+ }
+ U := &infos[u]
+ switch {
+ case T.isInterface && U.isInterface:
+ if satisfies(&U.base, &T.base) {
+ to = append(to, U.base.typ)
+ }
+ if satisfies(&T.base, &U.base) {
+ from = append(from, U.base.typ)
+ }
+ case T.isInterface: // U concrete
+ if satisfies(&U.base, &T.base) {
+ to = append(to, U.base.typ)
+ } else if satisfies(&U.ptr, &T.base) {
+ to = append(to, U.ptr.typ)
+ }
+ case U.isInterface: // T concrete
+ if satisfies(&T.base, &U.base) {
+ from = append(from, U.base.typ)
+ } else if satisfies(&T.ptr, &U.base) {
+ fromPtr = append(fromPtr, U.base.typ)
+ }
+ }
+ }
+
+ // Sort types (arbitrarily) to avoid nondeterminism.
+ sort.Sort(typesByString(to))
+ sort.Sort(typesByString(from))
+ sort.Sort(typesByString(fromPtr))
+
+ facts[T.base.typ.(*types.Named)] = implementsFacts{to, from, fromPtr}
+ }
+
+ return facts
+}
+
+type implementsFacts struct {
+ to []types.Type // named or ptr-to-named types assignable to interface T
+ from []types.Type // named interfaces assignable from T
+ fromPtr []types.Type // named interfaces assignable only from *T
+}
+
+type typesByString []types.Type
+
+func (p typesByString) Len() int { return len(p) }
+func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
+func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+// methodBit returns the index of x in [a-zA-Z], or 52 if not found.
+func methodBit(x byte) uint64 {
+ switch {
+ case 'a' <= x && x <= 'z':
+ return uint64(x - 'a')
+ case 'A' <= x && x <= 'Z':
+ return uint64(26 + x - 'A')
+ }
+ return 52 // all other bytes
+}
+
+// containsAllIdsOf reports whether the method identifiers of T are a
+// superset of those in U. If U belongs to an interface type, the
+// result is equal to types.Assignable(T, U), but is cheaper to compute.
+//
+// TODO(gri): make this a method of *types.MethodSet.
+//
+func containsAllIdsOf(T, U *types.MethodSet) bool {
+ t, tlen := 0, T.Len()
+ u, ulen := 0, U.Len()
+ for t < tlen && u < ulen {
+ tMeth := T.At(t).Obj()
+ uMeth := U.At(u).Obj()
+ tId := tMeth.Id()
+ uId := uMeth.Id()
+ if tId > uId {
+ // U has a method T lacks: fail.
+ return false
+ }
+ if tId < uId {
+ // T has a method U lacks: ignore it.
+ t++
+ continue
+ }
+ // U and T both have a method of this Id. Check types.
+ if !types.Identical(tMeth.Type(), uMeth.Type()) {
+ return false // type mismatch
+ }
+ u++
+ t++
+ }
+ return u == ulen
+}