//processDefs going through all definitions in the next order: // - collect info about all interfaces // - process everuthing except vars and functions to collect all structs prior vars and functions // - process vars and functions func processDefs(info *types.Info, ctx *getDefinitionsContext) { //Collect all interfaces for ident, obj := range info.Defs { if !isValidObject(obj, ident, ctx) || !types.IsInterface(obj.Type()) { continue } addInterface(obj, ident, ctx) } logInterfaces(ctx) //Collect everything except vars and functions for ident, obj := range info.Defs { if !isValidObject(obj, ident, ctx) { continue } var def *Definition if !isVar(obj) && !isFunc(obj) && !types.IsInterface(obj.Type()) { def = createDef(obj, ident, ctx, false) } updateGetDefinitionsContext(ctx, def, ident, obj) } for _, v := range ctx.vars { createDef(v.obj, v.ident, ctx, false) } for _, v := range ctx.funcs { createDef(v.obj, v.ident, ctx, false) } }
// IntuitiveMethodSet returns the intuitive method set of a type T, // which is the set of methods you can call on an addressable value of // that type. // // The result always contains MethodSet(T), and is exactly MethodSet(T) // for interface types and for pointer-to-concrete types. // For all other concrete types T, the result additionally // contains each method 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 *MethodSetCache) []*types.Selection { isPointerToConcrete := func(T types.Type) bool { ptr, ok := T.(*types.Pointer) return ok && !types.IsInterface(ptr.Elem()) } var result []*types.Selection mset := msets.MethodSet(T) if types.IsInterface(T) || isPointerToConcrete(T) { for i, n := 0, mset.Len(); i < n; i++ { result = append(result, mset.At(i)) } } else { // T is some other concrete type. // Report methods of T and *T, preferring those of T. 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 }
func createDef(obj types.Object, ident *ast.Ident, ctx *getDefinitionsContext, isType bool) *Definition { fullName := getFullName(obj, ctx, isType) if def, ok := ctx.defs[fullName]; ok { return def } def := new(Definition) def.Name = fullName def.Pkg = obj.Pkg() def.IsExported = obj.Exported() def.TypeOf = reflect.TypeOf(obj) def.SimpleName = obj.Name() def.Usages = make([]*Usage, 0) def.InterfacesDefs = make([]*Definition, 0) if ident != nil { position := ctx.fset.Position(ident.Pos()) def.File = position.Filename def.Line = position.Line def.Offset = position.Offset def.Col = position.Column } if !types.IsInterface(obj.Type()) { fillInterfaces(def, obj, ctx) } ctx.defs[def.Name] = def logDefinition(def, obj, ident, ctx) return def }
//processTypes is only filling interfaces from function signatures. func processTypes(info *types.Info, ctx *context) { for _, t := range info.Types { logType(t) if t.Type != nil { switch t.Type.(type) { //If it's a function signature then extracting //all params and trying to find interfaces. //We are doing this to find usages of interfaces //cause methods of the interfaces could be called // inside internal functions case *types.Signature: s := t.Type.(*types.Signature) if tuple := s.Params(); tuple != nil { for i := 0; i < tuple.Len(); i++ { v := tuple.At(i) if types.IsInterface(v.Type()) { addInterface(v, nil, ctx) } } } } } } }
func main() { // Parse one file. fset := token.NewFileSet() f, err := parser.ParseFile(fset, "input.go", input, 0) if err != nil { log.Fatal(err) // parse error } conf := types.Config{Importer: importer.Default()} pkg, err := conf.Check("hello", fset, []*ast.File{f}, nil) if err != nil { log.Fatal(err) // type error } //!+implements // Find all named types at package level. var allNamed []*types.Named for _, name := range pkg.Scope().Names() { if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok { allNamed = append(allNamed, obj.Type().(*types.Named)) } } // Test assignability of all distinct pairs of // named types (T, U) where U is an interface. for _, T := range allNamed { for _, U := range allNamed { if T == U || !types.IsInterface(U) { continue } if types.AssignableTo(T, U) { fmt.Printf("%s satisfies %s\n", T, U) } else if !types.IsInterface(T) && types.AssignableTo(types.NewPointer(T), U) { fmt.Printf("%s satisfies %s\n", types.NewPointer(T), U) } } } //!-implements }
func methodDocComment(prog *loader.Program, tname *types.TypeName, methodName string) (string, error) { t := tname.Type() if !types.IsInterface(t) { // Use the pointer type to get as many methods as possible. t = types.NewPointer(t) } mset := types.NewMethodSet(t) sel := mset.Lookup(nil, methodName) if sel == nil { return "", errgo.Newf("cannot find method %v on %v", methodName, t) } obj := sel.Obj() decl, err := findDecl(prog, obj.Pos()) if err != nil { return "", errgo.Mask(err) } switch decl := decl.(type) { case *ast.GenDecl: if decl.Tok != token.TYPE { return "", errgo.Newf("found non-type decl %#v", decl) } for _, spec := range decl.Specs { tspec := spec.(*ast.TypeSpec) it := tspec.Type.(*ast.InterfaceType) for _, m := range it.Methods.List { for _, id := range m.Names { if id.Pos() == obj.Pos() { return m.Doc.Text(), nil } } } } return "", errgo.Newf("method definition not found in type") case *ast.FuncDecl: if decl.Name.Pos() != obj.Pos() { return "", errgo.Newf("method definition not found (at %#v)", prog.Fset.Position(obj.Pos())) } return decl.Doc.Text(), nil default: return "", errgo.Newf("unexpected declaration %T found", decl) } }
// NewGenerator initializes a Generator that will mock the given interface from the specified package. func NewGenerator(pkg, iface string) (*Generator, error) { p, err := importer.DefaultWithTestFiles().Import(pkg) if err != nil { return nil, err } obj := p.Scope().Lookup(iface) if obj == nil { return nil, fmt.Errorf("interface %s missing", iface) } if !types.IsInterface(obj.Type()) { return nil, fmt.Errorf("%s should be an interface, was %s", iface, obj.Type()) } g := &Generator{ ifaceName: iface, pkg: p, iface: obj.Type().Underlying().(*types.Interface).Complete(), } g.SetTemplate(defaultMockTemplate) return g, nil }
func logType(t types.TypeAndValue) { if t.Type != nil { util.Debug("type [%s] [%s] [%s] [%s]", reflect.TypeOf(t.Type), t.Type.String(), reflect.TypeOf(t.Type.Underlying()), t.Type.Underlying().String()) switch t.Type.(type) { case *types.Signature: s := t.Type.(*types.Signature) if s.Recv() != nil { util.Info("\t\t[%s] [%s]", s.Recv(), s.Recv().Type().String()) } if tuple := s.Params(); tuple != nil { for i := 0; i < tuple.Len(); i++ { v := tuple.At(i) util.Debug("\t\t%s", v.Name()) if types.IsInterface(v.Type()) { util.Debug("\t\t\t<------interface") } } } } } }
func (p *exporter) typ(t types.Type) { if t == nil { log.Fatalf("gcimporter: nil type") } // Possible optimization: Anonymous pointer types *T where // T is a named type are common. We could canonicalize all // such types *T to a single type PT = *T. This would lead // to at most one *T entry in typIndex, and all future *T's // would be encoded as the respective index directly. Would // save 1 byte (pointerTag) per *T and reduce the typIndex // size (at the cost of a canonicalization map). We can do // this later, without encoding format change. // if we saw the type before, write its index (>= 0) if i, ok := p.typIndex[t]; ok { p.index('T', i) return } // otherwise, remember the type, write the type tag (< 0) and type data if trackAllTypes { if trace { p.tracef("T%d = {>\n", len(p.typIndex)) defer p.tracef("<\n} ") } p.typIndex[t] = len(p.typIndex) } switch t := t.(type) { case *types.Named: if !trackAllTypes { // if we don't track all types, track named types now p.typIndex[t] = len(p.typIndex) } p.tag(namedTag) p.pos(t.Obj()) p.qualifiedName(t.Obj()) p.typ(t.Underlying()) if !types.IsInterface(t) { p.assocMethods(t) } case *types.Array: p.tag(arrayTag) p.int64(t.Len()) p.typ(t.Elem()) case *types.Slice: p.tag(sliceTag) p.typ(t.Elem()) case *dddSlice: p.tag(dddTag) p.typ(t.elem) case *types.Struct: p.tag(structTag) p.fieldList(t) case *types.Pointer: p.tag(pointerTag) p.typ(t.Elem()) case *types.Signature: p.tag(signatureTag) p.paramList(t.Params(), t.Variadic()) p.paramList(t.Results(), false) case *types.Interface: p.tag(interfaceTag) p.iface(t) case *types.Map: p.tag(mapTag) p.typ(t.Key()) p.typ(t.Elem()) case *types.Chan: p.tag(chanTag) p.int(int(3 - t.Dir())) // hack p.typ(t.Elem()) default: log.Fatalf("gcimporter: unexpected type %T: %s", t, t) } }
func isInterface(T types.Type) bool { return types.IsInterface(T) }
// parent is the package which declared the type; parent == nil means // the package currently imported. The parent package is needed for // exported struct fields and interface methods which don't contain // explicit package information in the export data. func (p *importer) typ(parent *types.Package) types.Type { // if the type was seen before, i is its index (>= 0) i := p.tagOrIndex() if i >= 0 { return p.typList[i] } // otherwise, i is the type tag (< 0) switch i { case namedTag: // read type object pos := p.pos() parent, name := p.qualifiedName() scope := parent.Scope() obj := scope.Lookup(name) // if the object doesn't exist yet, create and insert it if obj == nil { obj = types.NewTypeName(pos, parent, name, nil) scope.Insert(obj) } if _, ok := obj.(*types.TypeName); !ok { panic(fmt.Sprintf("pkg = %s, name = %s => %s", parent, name, obj)) } // associate new named type with obj if it doesn't exist yet t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) // but record the existing type, if any t := obj.Type().(*types.Named) p.record(t) // read underlying type t0.SetUnderlying(p.typ(parent)) // interfaces don't have associated methods if types.IsInterface(t0) { return t } // read associated methods for i := p.int(); i > 0; i-- { // TODO(gri) replace this with something closer to fieldName pos := p.pos() name := p.string() if !exported(name) { p.pkg() } recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? params, isddd := p.paramList() result, _ := p.paramList() if p.version == "v1" { p.int() // nointerface flag - discarded } sig := types.NewSignature(recv.At(0), params, result, isddd) t0.AddMethod(types.NewFunc(pos, parent, name, sig)) } return t case arrayTag: t := new(types.Array) if p.trackAllTypes { p.record(t) } n := p.int64() *t = *types.NewArray(p.typ(parent), n) return t case sliceTag: t := new(types.Slice) if p.trackAllTypes { p.record(t) } *t = *types.NewSlice(p.typ(parent)) return t case dddTag: t := new(dddSlice) if p.trackAllTypes { p.record(t) } t.elem = p.typ(parent) return t case structTag: t := new(types.Struct) if p.trackAllTypes { p.record(t) } *t = *types.NewStruct(p.fieldList(parent)) return t case pointerTag: t := new(types.Pointer) if p.trackAllTypes { p.record(t) } *t = *types.NewPointer(p.typ(parent)) return t case signatureTag: t := new(types.Signature) if p.trackAllTypes { p.record(t) } params, isddd := p.paramList() result, _ := p.paramList() *t = *types.NewSignature(nil, params, result, isddd) return t case interfaceTag: // Create a dummy entry in the type list. This is safe because we // cannot expect the interface type to appear in a cycle, as any // such cycle must contain a named type which would have been // first defined earlier. n := len(p.typList) if p.trackAllTypes { p.record(nil) } // no embedded interfaces with gc compiler if p.int() != 0 { panic("unexpected embedded interface") } t := types.NewInterface(p.methodList(parent), nil) if p.trackAllTypes { p.typList[n] = t } return t case mapTag: t := new(types.Map) if p.trackAllTypes { p.record(t) } key := p.typ(parent) val := p.typ(parent) *t = *types.NewMap(key, val) return t case chanTag: t := new(types.Chan) if p.trackAllTypes { p.record(t) } var dir types.ChanDir // tag values must match the constants in cmd/compile/internal/gc/go.go switch d := p.int(); d { case 1 /* Crecv */ : dir = types.RecvOnly case 2 /* Csend */ : dir = types.SendOnly case 3 /* Cboth */ : dir = types.SendRecv default: panic(fmt.Sprintf("unexpected channel dir %d", d)) } val := p.typ(parent) *t = *types.NewChan(dir, val) return t default: panic(fmt.Sprintf("unexpected type tag %d", i)) } }
// Callees reports the possible callees of the function call site // identified by the specified source location. func callees(q *Query) error { lconf := loader.Config{Build: q.Build} if err := setPTAScope(&lconf, q.Scope); err != nil { return err } // Load/parse/type-check the program. lprog, err := lconf.Load() if err != nil { return err } q.Fset = lprog.Fset qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos if err != nil { return err } // Determine the enclosing call for the specified position. var e *ast.CallExpr for _, n := range qpos.path { if e, _ = n.(*ast.CallExpr); e != nil { break } } if e == nil { return fmt.Errorf("there is no function call here") } // TODO(adonovan): issue an error if the call is "too far // away" from the current selection, as this most likely is // not what the user intended. // Reject type conversions. if qpos.info.Types[e.Fun].IsType() { return fmt.Errorf("this is a type conversion, not a function call") } // Deal with obviously static calls before constructing SSA form. // Some static calls may yet require SSA construction, // e.g. f := func(){}; f(). switch funexpr := unparen(e.Fun).(type) { case *ast.Ident: switch obj := qpos.info.Uses[funexpr].(type) { case *types.Builtin: // Reject calls to built-ins. return fmt.Errorf("this is a call to the built-in '%s' operator", obj.Name()) case *types.Func: // This is a static function call q.result = &calleesTypesResult{ site: e, callee: obj, } return nil } case *ast.SelectorExpr: sel := qpos.info.Selections[funexpr] if sel == nil { // qualified identifier. // May refer to top level function variable // or to top level function. callee := qpos.info.Uses[funexpr.Sel] if obj, ok := callee.(*types.Func); ok { q.result = &calleesTypesResult{ site: e, callee: obj, } return nil } } else if sel.Kind() == types.MethodVal { // Inspect the receiver type of the selected method. // If it is concrete, the call is statically dispatched. // (Due to implicit field selections, it is not enough to look // at sel.Recv(), the type of the actual receiver expression.) method := sel.Obj().(*types.Func) recvtype := method.Type().(*types.Signature).Recv().Type() if !types.IsInterface(recvtype) { // static method call q.result = &calleesTypesResult{ site: e, callee: method, } return nil } } } prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug) ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection) if err != nil { return err } pkg := prog.Package(qpos.info.Pkg) if pkg == nil { return fmt.Errorf("no SSA package") } // Defer SSA construction till after errors are reported. prog.Build() // Ascertain calling function and call site. callerFn := ssa.EnclosingFunction(pkg, qpos.path) if callerFn == nil { return fmt.Errorf("no SSA function built for this location (dead code?)") } // Find the call site. site, err := findCallSite(callerFn, e) if err != nil { return err } funcs, err := findCallees(ptaConfig, site) if err != nil { return err } q.result = &calleesSSAResult{ site: site, funcs: funcs, } return nil }
// CreateTestMainPackage creates and returns a synthetic "testmain" // package for the specified package if it defines tests, benchmarks or // executable examples, or nil otherwise. The new package is named // "main" and provides a function named "main" that runs the tests, // similar to the one that would be created by the 'go test' tool. // // Subsequent calls to prog.AllPackages include the new package. // The package pkg must belong to the program prog. func (prog *Program) CreateTestMainPackage(pkg *Package) *Package { if pkg.Prog != prog { log.Fatal("Package does not belong to Program") } // Template data var data struct { Pkg *Package Tests, Benchmarks, Examples []*Function Main *Function Go18 bool } data.Pkg = pkg // Enumerate tests. data.Tests, data.Benchmarks, data.Examples, data.Main = FindTests(pkg) if data.Main == nil && data.Tests == nil && data.Benchmarks == nil && data.Examples == nil { return nil } // Synthesize source for testmain package. path := pkg.Pkg.Path() + "$testmain" tmpl := testmainTmpl if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil { // In Go 1.8, testing.MainStart's first argument is an interface, not a func. data.Go18 = types.IsInterface(testingPkg.Func("MainStart").Signature.Params().At(0).Type()) } else { // The program does not import "testing", but FindTests // returned non-nil, which must mean there were Examples // but no Test, Benchmark, or TestMain functions. // We'll simply call them from testmain.main; this will // ensure they don't panic, but will not check any // "Output:" comments. // (We should not execute an Example that has no // "Output:" comment, but it's impossible to tell here.) tmpl = examplesOnlyTmpl } var buf bytes.Buffer if err := tmpl.Execute(&buf, data); err != nil { log.Fatalf("internal error expanding template for %s: %v", path, err) } if false { // debugging fmt.Fprintln(os.Stderr, buf.String()) } // Parse and type-check the testmain package. f, err := parser.ParseFile(prog.Fset, path+".go", &buf, parser.Mode(0)) if err != nil { log.Fatalf("internal error parsing %s: %v", path, err) } conf := types.Config{ DisableUnusedImportCheck: true, Importer: importer{pkg}, } files := []*ast.File{f} info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), Scopes: make(map[ast.Node]*types.Scope), Selections: make(map[*ast.SelectorExpr]*types.Selection), } testmainPkg, err := conf.Check(path, prog.Fset, files, info) if err != nil { log.Fatalf("internal error type-checking %s: %v", path, err) } // Create and build SSA code. testmain := prog.CreatePackage(testmainPkg, files, info, false) testmain.SetDebugMode(false) testmain.Build() testmain.Func("main").Synthetic = "test main function" testmain.Func("init").Synthetic = "package initializer" return testmain }