func WriteInterfaces(dependencies []*types.Package, w io.Writer, merge bool) { allTypeNames := []*types.TypeName{types.New("error").(*types.Named).Obj()} for _, dep := range dependencies { scope := dep.Scope() for _, name := range scope.Names() { if typeName, isTypeName := scope.Lookup(name).(*types.TypeName); isTypeName { allTypeNames = append(allTypeNames, typeName) } } } for _, t := range allTypeNames { if in, isInterface := t.Type().Underlying().(*types.Interface); isInterface { if in.MethodSet().Len() == 0 { continue } implementedBy := make(map[string]bool, 0) for _, other := range allTypeNames { otherType := other.Type() switch otherType.Underlying().(type) { case *types.Interface: // skip case *types.Struct: if types.IsAssignableTo(otherType, in) { implementedBy[fmt.Sprintf("Go$packages[\"%s\"].%s.Go$NonPointer", other.Pkg().Path(), other.Name())] = true } if types.IsAssignableTo(types.NewPointer(otherType), in) { implementedBy[fmt.Sprintf("Go$packages[\"%s\"].%s", other.Pkg().Path(), other.Name())] = true } default: if types.IsAssignableTo(otherType, in) { implementedBy[fmt.Sprintf("Go$packages[\"%s\"].%s", other.Pkg().Path(), other.Name())] = true } if types.IsAssignableTo(types.NewPointer(otherType), in) { implementedBy[fmt.Sprintf("Go$packages[\"%s\"].%s.Go$Pointer", other.Pkg().Path(), other.Name())] = true } } } list := make([]string, 0, len(implementedBy)) for ref := range implementedBy { list = append(list, ref) } sort.Strings(list) var target string switch t.Name() { case "error": target = "Go$error" default: target = fmt.Sprintf("Go$packages[\"%s\"].%s", t.Pkg().Path(), t.Name()) } if merge { for _, entry := range list { fmt.Fprintf(w, "if (%s.Go$implementedBy.indexOf(%s) === -1) { %s.Go$implementedBy.push(%s); }\n", target, entry, target, entry) } continue } fmt.Fprintf(w, "%s.Go$implementedBy = [%s];\n", target, strings.Join(list, ", ")) } } }
func (c *typeAssertConstraint) solve(a *analysis, n *node, delta nodeset) { tIface, _ := c.typ.Underlying().(*types.Interface) for ifaceObj := range delta { tDyn, v, indirect := a.taggedValue(ifaceObj) if tDyn == nil { panic("not a tagged value") } if indirect { // TODO(adonovan): we'll need to implement this // when we start creating indirect tagged objects. panic("indirect tagged object") } if tIface != nil { if types.IsAssignableTo(tDyn, tIface) { if a.addLabel(c.dst, ifaceObj) { a.addWork(c.dst) } } } else { if types.IsIdentical(tDyn, c.typ) { // Copy entire payload to dst. // // TODO(adonovan): opt: if tConc is // nonpointerlike we can skip this // entire constraint, perhaps. We // only care about pointers among the // fields. a.onlineCopyN(c.dst, v, a.sizeof(tDyn)) } } } }
// Implements displays the 'implements" relation among all // package-level named types in the package containing the query // position. // // TODO(adonovan): more features: // - should we include pairs of types belonging to // different packages in the 'implements' relation? // - should we restrict the query to the type declaration identified // by the query position, if any, and use all types in the package // otherwise? // - should we show types that are local to functions? // They can only have methods via promotion. // - abbreviate the set of concrete types implementing the empty // interface. // - should we scan the instruction stream for MakeInterface // instructions and report which concrete->interface conversions // actually occur, with examples? (NB: this is not a conservative // answer due to ChangeInterface, i.e. subtyping among interfaces.) // func implements(o *Oracle, qpos *QueryPos) (queryResult, error) { pkg := qpos.info.Pkg // Compute set of named interface/concrete types at package level. var interfaces, concretes []*types.Named scope := pkg.Scope() for _, name := range scope.Names() { mem := scope.Lookup(name) if t, ok := mem.(*types.TypeName); ok { nt := t.Type().(*types.Named) if _, ok := nt.Underlying().(*types.Interface); ok { interfaces = append(interfaces, nt) } else { concretes = append(concretes, nt) } } } // For each interface, show the concrete types that implement it. var facts []implementsFact for _, iface := range interfaces { fact := implementsFact{iface: iface} for _, conc := range concretes { if types.IsAssignableTo(conc, iface) { fact.conc = conc } else if ptr := types.NewPointer(conc); types.IsAssignableTo(ptr, iface) { fact.conc = ptr } else { continue } facts = append(facts, fact) } } // TODO(adonovan): sort facts to ensure test nondeterminism. return &implementsResult{o.prog.Fset, facts}, nil }
func (c *typeFilterConstraint) solve(a *analysis, n *node, delta nodeset) { for ifaceObj := range delta { tDyn, _, indirect := a.taggedValue(ifaceObj) if indirect { // TODO(adonovan): we'll need to implement this // when we start creating indirect tagged objects. panic("indirect tagged object") } if types.IsAssignableTo(tDyn, c.typ) { if a.addLabel(c.dst, ifaceObj) { a.addWork(c.dst) } } } }
// 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 id, obj := range info.Objects { if obj, ok := obj.(*types.TypeName); ok && obj.Pos() == id.Pos() { allNamed = append(allNamed, obj.Type()) } } } allNamed = append(allNamed, types.Universe.Lookup("error").Type()) // Test each named type. var to, from, fromPtr []types.Type for _, U := range allNamed { if isInterface(T) { if T.MethodSet().Len() == 0 { continue // empty interface } if isInterface(U) { if U.MethodSet().Len() == 0 { continue // empty interface } // T interface, U interface if !types.IsIdentical(T, U) { if types.IsAssignableTo(U, T) { to = append(to, U) } if types.IsAssignableTo(T, U) { from = append(from, U) } } } else { // T interface, U concrete if types.IsAssignableTo(U, T) { to = append(to, U) } else if pU := types.NewPointer(U); types.IsAssignableTo(pU, T) { to = append(to, pU) } } } else if isInterface(U) { if U.MethodSet().Len() == 0 { continue // empty interface } // T concrete, U interface if types.IsAssignableTo(T, U) { from = append(from, U) } else if pT := types.NewPointer(T); types.IsAssignableTo(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 nondeterminism. sort.Sort(typesByString(to)) sort.Sort(typesByString(from)) sort.Sort(typesByString(fromPtr)) return &implementsResult{T, pos, to, from, fromPtr}, nil }