// interfaces(C) returns all currently known interfaces implemented by C. func (r *rta) interfaces(C types.Type) []*types.Interface { // Ascertain set of interfaces C implements // and update 'implements' relation. var ifaces []*types.Interface r.interfaceTypes.Iterate(func(I types.Type, concs interface{}) { if I := I.(*types.Interface); types.Implements(C, I) { concs, _ := concs.([]types.Type) r.interfaceTypes.Set(I, append(concs, C)) ifaces = append(ifaces, I) } }) r.concreteTypes.Set(C, ifaces) return ifaces }
// implementations(I) returns all currently known concrete types that implement I. func (r *rta) implementations(I *types.Interface) []types.Type { var concs []types.Type if v := r.interfaceTypes.At(I); v != nil { concs = v.([]types.Type) } else { // First time seeing this interface. // Update the 'implements' relation. r.concreteTypes.Iterate(func(C types.Type, ifaces interface{}) { if types.Implements(C, I) { ifaces, _ := ifaces.([]*types.Interface) r.concreteTypes.Set(C, append(ifaces, I)) concs = append(concs, C) } }) r.interfaceTypes.Set(I, concs) } return concs }
func implements(t types.Type, interfac *types.Interface, pkg *types.Package) bool { if interfac == nil || t == nil || interfac.Empty() { return false } if types.Implements(t, interfac) { return true } //For some reason, interfaces that comes //already built in (not from sources) are //not working with types.Implements method for i := 0; i < interfac.NumMethods(); i++ { m := interfac.Method(i) obj, _, _ := types.LookupFieldOrMethod(t, true, pkg, m.Name()) if obj == nil { util.Debug("method %s not found in type %v", m.Name(), t) return false } } return true }
// analyzeCode takes the types scope and the docs and returns the import // information and information about all the assertion functions. func analyzeCode(scope *types.Scope, docs *doc.Package) (imports.Importer, []testFunc, error) { testingT := scope.Lookup("TestingT").Type().Underlying().(*types.Interface) importer := imports.New(*outputPkg) var funcs []testFunc // Go through all the top level functions for _, fdocs := range docs.Funcs { // Find the function obj := scope.Lookup(fdocs.Name) fn, ok := obj.(*types.Func) if !ok { continue } // Check function signatuer has at least two arguments sig := fn.Type().(*types.Signature) if sig.Params().Len() < 2 { continue } // Check first argument is of type testingT first, ok := sig.Params().At(0).Type().(*types.Named) if !ok { continue } firstType, ok := first.Underlying().(*types.Interface) if !ok { continue } if !types.Implements(firstType, testingT) { continue } funcs = append(funcs, testFunc{*outputPkg, fdocs, fn}) importer.AddImportsFrom(sig.Params()) } return importer, funcs, nil }
// isFormatter reports whether t satisfies fmt.Formatter. // Unlike fmt.Stringer, it's impossible to satisfy fmt.Formatter without importing fmt. func (f *File) isFormatter(t types.Type) bool { return formatterType != nil && types.Implements(t, formatterType) }
// matchArgTypeInternal is the internal version of matchArgType. It carries a map // remembering what types are in progress so we don't recur when faced with recursive // types or mutually recursive types. func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool { // %v, %T accept any argument type. if t == anyType { return true } if typ == nil { // external call typ = f.pkg.types[arg].Type if typ == nil { return true // probably a type check problem } } // If the type implements fmt.Formatter, we have nothing to check. // formatterTyp may be nil - be conservative and check for Format method in that case. if formatterType != nil && types.Implements(typ, formatterType) || f.hasMethod(typ, "Format") { return true } // If we can use a string, might arg (dynamically) implement the Stringer or Error interface? if t&argString != 0 { if types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ) { return true } } typ = typ.Underlying() if inProgress[typ] { // We're already looking at this type. The call that started it will take care of it. return true } inProgress[typ] = true switch typ := typ.(type) { case *types.Signature: return t&argPointer != 0 case *types.Map: // Recur: map[int]int matches %d. return t&argPointer != 0 || (f.matchArgTypeInternal(t, typ.Key(), arg, inProgress) && f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress)) case *types.Chan: return t&argPointer != 0 case *types.Array: // Same as slice. if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { return true // %s matches []byte } // Recur: []int matches %d. return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress) case *types.Slice: // Same as array. if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { return true // %s matches []byte } // Recur: []int matches %d. But watch out for // type T []T // If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below. return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress) case *types.Pointer: // Ugly, but dealing with an edge case: a known pointer to an invalid type, // probably something from a failed import. if typ.Elem().String() == "invalid type" { if *verbose { f.Warnf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", f.gofmt(arg)) } return true // special case } // If it's actually a pointer with %p, it prints as one. if t == argPointer { return true } // If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct. if str, ok := typ.Elem().Underlying().(*types.Struct); ok { return f.matchStructArgType(t, str, arg, inProgress) } // The rest can print with %p as pointers, or as integers with %x etc. return t&(argInt|argPointer) != 0 case *types.Struct: return f.matchStructArgType(t, typ, arg, inProgress) case *types.Interface: // If the static type of the argument is empty interface, there's little we can do. // Example: // func f(x interface{}) { fmt.Printf("%s", x) } // Whether x is valid for %s depends on the type of the argument to f. One day // we will be able to do better. For now, we assume that empty interface is OK // but non-empty interfaces, with Stringer and Error handled above, are errors. return typ.NumMethods() == 0 case *types.Basic: switch typ.Kind() { case types.UntypedBool, types.Bool: return t&argBool != 0 case types.UntypedInt, types.Int, types.Int8, types.Int16, types.Int32, types.Int64, types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: return t&argInt != 0 case types.UntypedFloat, types.Float32, types.Float64: return t&argFloat != 0 case types.UntypedComplex, types.Complex64, types.Complex128: return t&argComplex != 0 case types.UntypedString, types.String: return t&argString != 0 case types.UnsafePointer: return t&(argPointer|argInt) != 0 case types.UntypedRune: return t&(argInt|argRune) != 0 case types.UntypedNil: return t&argPointer != 0 // TODO? case types.Invalid: if *verbose { f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg)) } return true // Probably a type check problem. } panic("unreachable") } return false }
// CallGraph computes the call graph of the specified program using the // Class Hierarchy Analysis algorithm. // func CallGraph(prog *ssa.Program) *callgraph.Graph { cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph allFuncs := ssautil.AllFunctions(prog) // funcsBySig contains all functions, keyed by signature. It is // the effective set of address-taken functions used to resolve // a dynamic call of a particular signature. var funcsBySig typeutil.Map // value is []*ssa.Function // methodsByName contains all methods, // grouped by name for efficient lookup. methodsByName := make(map[string][]*ssa.Function) // methodsMemo records, for every abstract method call call I.f on // interface type I, the set of concrete methods C.f of all // types C that satisfy interface I. methodsMemo := make(map[*types.Func][]*ssa.Function) lookupMethods := func(m *types.Func) []*ssa.Function { methods, ok := methodsMemo[m] if !ok { I := m.Type().(*types.Signature).Recv().Type().Underlying().(*types.Interface) for _, f := range methodsByName[m.Name()] { C := f.Signature.Recv().Type() // named or *named if types.Implements(C, I) { methods = append(methods, f) } } methodsMemo[m] = methods } return methods } for f := range allFuncs { if f.Signature.Recv() == nil { // Package initializers can never be address-taken. if f.Name() == "init" && f.Synthetic == "package initializer" { continue } funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function) funcs = append(funcs, f) funcsBySig.Set(f.Signature, funcs) } else { methodsByName[f.Name()] = append(methodsByName[f.Name()], f) } } addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) { gnode := cg.CreateNode(g) callgraph.AddEdge(fnode, site, gnode) } addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) { // Because every call to a highly polymorphic and // frequently used abstract method such as // (io.Writer).Write is assumed to call every concrete // Write method in the program, the call graph can // contain a lot of duplication. // // TODO(adonovan): opt: consider factoring the callgraph // API so that the Callers component of each edge is a // slice of nodes, not a singleton. for _, g := range callees { addEdge(fnode, site, g) } } for f := range allFuncs { fnode := cg.CreateNode(f) for _, b := range f.Blocks { for _, instr := range b.Instrs { if site, ok := instr.(ssa.CallInstruction); ok { call := site.Common() if call.IsInvoke() { addEdges(fnode, site, lookupMethods(call.Method)) } else if g := call.StaticCallee(); g != nil { addEdge(fnode, site, g) } else if _, ok := call.Value.(*ssa.Builtin); !ok { callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function) addEdges(fnode, site, callees) } } } } } return cg }
// Is type src assignment compatible to type dst? // If so, return op code to use in conversion. // If not, return 0. func assignop(src *Type, dst *Type, why *string) NodeOp { if !types.AssignableTo(src.Type, dst.Type) { return 0 } if src == dst { return OCONVNOP } if src == nil || dst == nil { return 0 } // 1. src type is identical to dst. if Eqtype(src, dst) { return OCONVNOP } // 3. dst is an interface type and src implements dst. if dst.IsInterface() { dstInterface := dst.Type.(*types.Interface) if types.Implements(src.Type, dstInterface) { return OCONVIFACE } return 0 } if isptrto(dst, TINTER) { if why != nil { *why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst) } return 0 } if src.IsChan() || dst.IsChan() { panic("channels not supported") } // 5. src is the predeclared identifier nil and dst is a nillable type. if src.Etype() == TNIL { switch dst.Etype() { case TARRAY: if dst.Bound() != -100 { // not slice break } fallthrough case TPTR32, TPTR64, TFUNC, TMAP, TCHAN, TINTER: return OCONVNOP } } // 6. rule about untyped constants - already converted by defaultlit. // 7. Any typed value can be assigned to the blank identifier. if dst.Etype() == TBLANK { return OCONVNOP } return 0 }
func isErrorType(t types.Type) bool { return types.Implements(t, errorType) }