196 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
| // 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 (
 | |
| 	"sort"
 | |
| 
 | |
| 	"golang.org/x/tools/go/types"
 | |
| 	"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
 | |
| }
 |