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  }