// IntuitiveMethodSet returns the intuitive method set of a type, T. // // The result contains MethodSet(T) and additionally, if T is a // concrete type, methods belonging to *T if there is no identically // named method on T itself. This corresponds to user intuition about // method sets; this function is intended only for user interfaces. // // The order of the result is as for types.MethodSet(T). // func IntuitiveMethodSet(T types.Type, msets *types.MethodSetCache) []*types.Selection { var result []*types.Selection mset := msets.MethodSet(T) if _, ok := T.Underlying().(*types.Interface); ok { for i, n := 0, mset.Len(); i < n; i++ { result = append(result, mset.At(i)) } } else { pmset := msets.MethodSet(types.NewPointer(T)) for i, n := 0, pmset.Len(); i < n; i++ { meth := pmset.At(i) if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil { meth = m } result = append(result, meth) } } return result }
// computeImplements computes the "implements" relation over all pairs // of named types in allNamed. func computeImplements(cache *types.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 }
// Implements displays the "implements" relation as it pertains to the // selected type. // func implements(o *Oracle, qpos *QueryPos) (queryResult, error) { // Find the selected type. // TODO(adonovan): fix: make it work on qualified Idents too. path, action := findInterestingNode(qpos.info, qpos.path) if action != actionType { return nil, fmt.Errorf("no type here") } T := qpos.info.TypeOf(path[0].(ast.Expr)) if T == nil { return nil, fmt.Errorf("no type here") } // Find all named types, even local types (which can have // methods via promotion) and the built-in "error". // // TODO(adonovan): include all packages in PTA scope too? // i.e. don't reduceScope? // var allNamed []types.Type for _, info := range o.typeInfo { for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok { allNamed = append(allNamed, obj.Type()) } } } allNamed = append(allNamed, types.Universe.Lookup("error").Type()) var msets types.MethodSetCache // Test each named type. var to, from, fromPtr []types.Type for _, U := range allNamed { if isInterface(T) { if msets.MethodSet(T).Len() == 0 { continue // empty interface } if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T interface, U interface if !types.Identical(T, U) { if types.AssignableTo(U, T) { to = append(to, U) } if types.AssignableTo(T, U) { from = append(from, U) } } } else { // T interface, U concrete if types.AssignableTo(U, T) { to = append(to, U) } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) { to = append(to, pU) } } } else if isInterface(U) { if msets.MethodSet(U).Len() == 0 { continue // empty interface } // T concrete, U interface if types.AssignableTo(T, U) { from = append(from, U) } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) { fromPtr = append(fromPtr, U) } } } var pos interface{} = qpos if nt, ok := deref(T).(*types.Named); ok { pos = nt.Obj() } // Sort types (arbitrarily) to ensure test determinism. sort.Sort(typesByString(to)) sort.Sort(typesByString(from)) sort.Sort(typesByString(fromPtr)) return &implementsResult{T, pos, to, from, fromPtr}, nil }