// FindQueryMethods locates all methods in the given package (assumed to be // package database/sql) with a string parameter named "query". func FindQueryMethods(sql *types.Package, ssa *ssa.Program) []*QueryMethod { methods := make([]*QueryMethod, 0) scope := sql.Scope() for _, name := range scope.Names() { o := scope.Lookup(name) if !o.Exported() { continue } if _, ok := o.(*types.TypeName); !ok { continue } n := o.Type().(*types.Named) for i := 0; i < n.NumMethods(); i++ { m := n.Method(i) if !m.Exported() { continue } s := m.Type().(*types.Signature) if num, ok := FuncHasQuery(s); ok { methods = append(methods, &QueryMethod{ Func: m, SSA: ssa.FuncValue(m), ArgCount: s.Params().Len(), Param: num, }) } } } return methods }
func (c *converter) convertPackage(v *gotypes.Package) *types.Package { if v == nil { return nil } if v, ok := c.converted[v]; ok { return v.(*types.Package) } ret := types.NewPackage(v.Path(), v.Name()) if c.ret == nil { c.ret = ret } c.converted[v] = ret var imports []*types.Package for _, imported := range v.Imports() { imports = append(imports, c.convertPackage(imported)) } ret.SetImports(imports) c.convertScope(ret.Scope(), v.Scope()) for _, iface := range c.ifaces { iface.Complete() } return ret }
//!+ func PrintSkeleton(pkg *types.Package, ifacename, concname string) error { obj := pkg.Scope().Lookup(ifacename) if obj == nil { return fmt.Errorf("%s.%s not found", pkg.Path(), ifacename) } if _, ok := obj.(*types.TypeName); !ok { return fmt.Errorf("%v is not a named type", obj) } iface, ok := obj.Type().Underlying().(*types.Interface) if !ok { return fmt.Errorf("type %v is a %T, not an interface", obj, obj.Type().Underlying()) } // Use first letter of type name as receiver parameter. if !isValidIdentifier(concname) { return fmt.Errorf("invalid concrete type name: %q", concname) } r, _ := utf8.DecodeRuneInString(concname) fmt.Printf("// *%s implements %s.%s.\n", concname, pkg.Path(), ifacename) fmt.Printf("type %s struct{}\n", concname) mset := types.NewMethodSet(iface) for i := 0; i < mset.Len(); i++ { meth := mset.At(i).Obj() sig := types.TypeString(meth.Type(), (*types.Package).Name) fmt.Printf("func (%c *%s) %s%s {\n\tpanic(\"unimplemented\")\n}\n", r, concname, meth.Name(), strings.TrimPrefix(sig, "func")) } return nil }
func find(pkg *types.Package, iface string) (*types.Interface, error) { scope := pkg.Scope() names := scope.Names() for _, n := range names { obj := scope.Lookup(n) tn, ok := obj.(*types.TypeName) if !ok { continue } if tn.Name() != iface { continue } if !obj.Exported() { return nil, fmt.Errorf("%s should exported", iface) } t := tn.Type().Underlying() i, ok := t.(*types.Interface) if !ok { return nil, fmt.Errorf("exptected interface, got %s for %s", t, iface) } return i, nil } return nil, fmt.Errorf("%s not found in %s", iface, pkg.Name()) }
func describePackage(qpos *queryPos, path []ast.Node) (*describePackageResult, error) { var description string var pkg *types.Package switch n := path[0].(type) { case *ast.ImportSpec: var obj types.Object if n.Name != nil { obj = qpos.info.Defs[n.Name] } else { obj = qpos.info.Implicits[n] } pkgname, _ := obj.(*types.PkgName) if pkgname == nil { return nil, fmt.Errorf("can't import package %s", n.Path.Value) } pkg = pkgname.Imported() description = fmt.Sprintf("import of package %q", pkg.Path()) case *ast.Ident: if _, isDef := path[1].(*ast.File); isDef { // e.g. package id pkg = qpos.info.Pkg description = fmt.Sprintf("definition of package %q", pkg.Path()) } else { // e.g. import id "..." // or id.F() pkg = qpos.info.ObjectOf(n).(*types.PkgName).Imported() description = fmt.Sprintf("reference to package %q", pkg.Path()) } default: // Unreachable? return nil, fmt.Errorf("unexpected AST for package: %T", n) } var members []*describeMember // NB: "unsafe" has no types.Package if pkg != nil { // Enumerate the accessible package members // in lexicographic order. for _, name := range pkg.Scope().Names() { if pkg == qpos.info.Pkg || ast.IsExported(name) { mem := pkg.Scope().Lookup(name) var methods []*types.Selection if mem, ok := mem.(*types.TypeName); ok { methods = accessibleMethods(mem.Type(), qpos.info.Pkg) } members = append(members, &describeMember{ mem, methods, }) } } } return &describePackageResult{qpos.fset, path[0], description, pkg, members}, nil }
func getMethods(pkg *types.Package, typename string) map[string]*types.Func { r := make(map[string]*types.Func) mset := types.NewMethodSet(types.NewPointer(pkg.Scope().Lookup(typename).Type())) for i := 0; i < mset.Len(); i++ { fn := mset.At(i).Obj().(*types.Func) r[fn.Name()] = fn } return r }
func declTypeName(pkg *types.Package, name string) *types.TypeName { scope := pkg.Scope() if obj := scope.Lookup(name); obj != nil { return obj.(*types.TypeName) } obj := types.NewTypeName(token.NoPos, pkg, name, nil) // a named type may be referred to before the underlying type // is known - set it up types.NewNamed(obj, nil, nil) scope.Insert(obj) return obj }
// export emits the exported package features. func (w *Walker) export(pkg *types.Package) { if *verbose { log.Println(pkg) } pop := w.pushScope("pkg " + pkg.Path()) w.current = pkg scope := pkg.Scope() for _, name := range scope.Names() { if ast.IsExported(name) { w.emitObj(scope.Lookup(name)) } } pop() }
// addOwners updates pi.owner from the types in pkg, adding mapping from fields // of package-level named struct types to the owning named struct type; from // methods of package-level named interface types to the owning named interface // type; and from parameters of package-level named function or method types to // the owning named function or method. // // This relation is used to construct signatures for these fields/methods, // since they may be referenced from another package and thus need // deterministic names. An object does expose its "owner"; indeed, it may have // several. // // Caveats: // // (1) This mapping is deterministic but not necessarily the best one according // to the original syntax, to which, in general, we do not have access. In // these two examples, the type checker considers field X as belonging equally // to types T and U, even though according the syntax, it belongs primarily to // T in the first example and U in the second: // // type T struct {X int} // type U T // // type T U // type U struct {X int} // // Similarly: // // type U struct {X int} // type V struct {U} // // TODO(adonovan): sameer@ points out a useful heuristic: in a case of struct // or interface embedding, if one struct/interface has fewer fields/methods, // then it must be the primary one. // // (2) This pass is not exhaustive: there remain objects that may be referenced // from outside the package but for which we can't easily come up with good // names. Here are some examples: // // // package p // var V1, V2 struct {X int} = ... // func F() struct{X int} {...} // type T struct { // Y struct { X int } // } // // // main // p.V2.X = 1 // print(p.F().X) // new(p.T).Y[0].X // // Also note that there may be arbitrary pointer, struct, chan, map, array, and // slice type constructors between the type of the exported package member (V2, // F or T) and the type of its X subelement. For now, we simply ignore such // names. They should be rare in readable code. func (pi *PackageInfo) addOwners(pkg *types.Package) { scope := pkg.Scope() for _, name := range scope.Names() { switch obj := scope.Lookup(name).(type) { case *types.TypeName: switch t := obj.Type().Underlying().(type) { case *types.Struct: // Inspect the fields of a struct. for i := 0; i < t.NumFields(); i++ { f := t.Field(i) if f.Pkg() != pkg { continue // wrong package } if _, ok := pi.owner[f]; !ok { pi.owner[f] = obj } } case *types.Interface: // Inspect the declared methods of an interface. for i := 0; i < t.NumMethods(); i++ { m := t.Method(i) if m.Pkg() != pkg { continue // wrong package } if _, ok := pi.owner[m]; !ok { pi.owner[m] = obj } } } case *types.Func: // Inspect the receiver, parameters, and result values. fsig := obj.Type().(*types.Signature) if recv := fsig.Recv(); recv != nil { pi.owner[recv] = obj } if params := fsig.Params(); params != nil { for i := 0; i < params.Len(); i++ { pi.owner[params.At(i)] = obj } } if res := fsig.Results(); res != nil { for i := 0; i < res.Len(); i++ { pi.owner[res.At(i)] = obj } } } } }
func TestIssue13898(t *testing.T) { skipSpecialPlatforms(t) // This package only handles gc export data. if runtime.Compiler != "gc" { t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) return } // import go/internal/gcimporter which imports go/types partially imports := make(map[string]*types.Package) _, err := Import(imports, "go/internal/gcimporter", ".") if err != nil { t.Fatal(err) } // look for go/types package var goTypesPkg *types.Package for path, pkg := range imports { if path == "go/types" { goTypesPkg = pkg break } } if goTypesPkg == nil { t.Fatal("go/types not found") } // look for go/types.Object type obj := goTypesPkg.Scope().Lookup("Object") if obj == nil { t.Fatal("go/types.Object not found") } typ, ok := obj.Type().(*types.Named) if !ok { t.Fatalf("go/types.Object type is %v; wanted named type", typ) } // lookup go/types.Object.Pkg method m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg") if m == nil { t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect) } // the method must belong to go/types if m.Pkg().Path() != "go/types" { t.Fatalf("found %v; want go/types", m.Pkg()) } }
func (p *importer) obj(pkg *types.Package) { var obj types.Object switch tag := p.int(); tag { case constTag: obj = types.NewConst(token.NoPos, pkg, p.string(), p.typ(), p.value()) case typeTag: // type object is added to scope via respective named type _ = p.typ().(*types.Named) return case varTag: obj = types.NewVar(token.NoPos, pkg, p.string(), p.typ()) case funcTag: obj = types.NewFunc(token.NoPos, pkg, p.string(), p.typ().(*types.Signature)) default: panic(fmt.Sprintf("unexpected object tag %d", tag)) } if alt := pkg.Scope().Insert(obj); alt != nil { panic(fmt.Sprintf("%s already declared", alt.Name())) } }
// ExportData serializes the interface (exported package objects) // of package pkg and returns the corresponding data. The export // format is described elsewhere (TODO). func ExportData(pkg *types.Package) []byte { p := exporter{ data: append([]byte(magic), format()), pkgIndex: make(map[*types.Package]int), typIndex: make(map[types.Type]int), } // populate typIndex with predeclared types for _, t := range predeclared { p.typIndex[t] = len(p.typIndex) } if trace { p.tracef("export %s\n", pkg.Name()) defer p.tracef("\n") } p.string(version) p.pkg(pkg) // collect exported objects from package scope var list []types.Object scope := pkg.Scope() for _, name := range scope.Names() { if exported(name) { list = append(list, scope.Lookup(name)) } } // write objects p.int(len(list)) for _, obj := range list { p.obj(obj) } return p.data }
// BExportData returns binary export data for pkg. // If no file set is provided, position info will be missing. func BExportData(fset *token.FileSet, pkg *types.Package) []byte { p := exporter{ fset: fset, strIndex: map[string]int{"": 0}, // empty string is mapped to 0 pkgIndex: make(map[*types.Package]int), typIndex: make(map[types.Type]int), posInfoFormat: true, // TODO(gri) might become a flag, eventually } // first byte indicates low-level encoding format var format byte = 'c' // compact if debugFormat { format = 'd' } p.rawByte(format) format = 'n' // track named types only if trackAllTypes { format = 'a' } p.rawByte(format) // posInfo exported or not? p.bool(p.posInfoFormat) // --- generic export data --- if trace { p.tracef("\n--- generic export data ---\n") if p.indent != 0 { log.Fatalf("gcimporter: incorrect indentation %d", p.indent) } } if trace { p.tracef("version = ") } p.string(exportVersion) if trace { p.tracef("\n") } // populate type map with predeclared "known" types for index, typ := range predeclared { p.typIndex[typ] = index } if len(p.typIndex) != len(predeclared) { log.Fatalf("gcimporter: duplicate entries in type map?") } // write package data p.pkg(pkg, true) if trace { p.tracef("\n") } // write objects objcount := 0 scope := pkg.Scope() for _, name := range scope.Names() { if !ast.IsExported(name) { continue } if trace { p.tracef("\n") } p.obj(scope.Lookup(name)) objcount++ } // indicate end of list if trace { p.tracef("\n") } p.tag(endTag) // for self-verification only (redundant) p.int(objcount) if trace { p.tracef("\n") } // --- end of export data --- return p.out.Bytes() }
func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) { // collect objects by kind var ( consts []*types.Const typem []*types.Named // non-interface types with methods typez []*types.TypeName // interfaces or types without methods vars []*types.Var funcs []*types.Func builtins []*types.Builtin methods = make(map[*types.Named][]*types.Selection) // method sets for named types ) scope := pkg.Scope() for _, name := range scope.Names() { obj := scope.Lookup(name) if obj.Exported() { // collect top-level exported and possibly filtered objects if filter == nil || filter(obj) { switch obj := obj.(type) { case *types.Const: consts = append(consts, obj) case *types.TypeName: // group into types with methods and types without if named, m := methodsFor(obj); named != nil { typem = append(typem, named) methods[named] = m } else { typez = append(typez, obj) } case *types.Var: vars = append(vars, obj) case *types.Func: funcs = append(funcs, obj) case *types.Builtin: // for unsafe.Sizeof, etc. builtins = append(builtins, obj) } } } else if filter == nil { // no filtering: collect top-level unexported types with methods if obj, _ := obj.(*types.TypeName); obj != nil { // see case *types.TypeName above if named, m := methodsFor(obj); named != nil { typem = append(typem, named) methods[named] = m } } } } p.printf("package %s // %q\n", pkg.Name(), pkg.Path()) p.printDecl("const", len(consts), func() { for _, obj := range consts { p.printObj(obj) p.print("\n") } }) p.printDecl("var", len(vars), func() { for _, obj := range vars { p.printObj(obj) p.print("\n") } }) p.printDecl("type", len(typez), func() { for _, obj := range typez { p.printf("%s ", obj.Name()) p.writeType(p.pkg, obj.Type().Underlying()) p.print("\n") } }) // non-interface types with methods for _, named := range typem { first := true if obj := named.Obj(); obj.Exported() { if first { p.print("\n") first = false } p.printf("type %s ", obj.Name()) p.writeType(p.pkg, named.Underlying()) p.print("\n") } for _, m := range methods[named] { if obj := m.Obj(); obj.Exported() { if first { p.print("\n") first = false } p.printFunc(m.Recv(), obj.(*types.Func)) p.print("\n") } } } if len(funcs) > 0 { p.print("\n") for _, obj := range funcs { p.printFunc(nil, obj) p.print("\n") } } // TODO(gri) better handling of builtins (package unsafe only) if len(builtins) > 0 { p.print("\n") for _, obj := range builtins { p.printf("func %s() // builtin\n", obj.Name()) } } p.print("\n") }
// BExportData returns binary export data for pkg. // If no file set is provided, position info will be missing. func BExportData(fset *token.FileSet, pkg *types.Package) []byte { p := exporter{ fset: fset, strIndex: map[string]int{"": 0}, // empty string is mapped to 0 pkgIndex: make(map[*types.Package]int), typIndex: make(map[types.Type]int), reexported: make(map[types.Object]bool), posInfoFormat: true, // TODO(gri) might become a flag, eventually } // write version info // The version string must start with "version %d" where %d is the version // number. Additional debugging information may follow after a blank; that // text is ignored by the importer. p.rawStringln(fmt.Sprintf("version %d", exportVersion)) var debug string if debugFormat { debug = "debug" } p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly p.bool(trackAllTypes) p.bool(p.posInfoFormat) // --- generic export data --- // populate type map with predeclared "known" types for index, typ := range predeclared { p.typIndex[typ] = index } if len(p.typIndex) != len(predeclared) { log.Fatalf("gcimporter: duplicate entries in type map?") } // write package data p.pkg(pkg, true) if trace { p.tracef("\n") } // write objects objcount := 0 scope := pkg.Scope() for _, name := range scope.Names() { if !ast.IsExported(name) { continue } if trace { p.tracef("\n") } p.obj(scope.Lookup(name)) objcount++ } // indicate end of list if trace { p.tracef("\n") } p.tag(endTag) // for self-verification only (redundant) p.int(objcount) if trace { p.tracef("\n") } // --- end of export data --- return p.out.Bytes() }
func (c *Suggester) packageCandidates(pkg *types.Package, b *candidateCollector) { c.scopeCandidates(pkg.Scope(), token.NoPos, b) }
// 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 { errorf("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() p.int() // go:nointerface pragma - 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 { errorf("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: errorf("unexpected channel dir %d", d) } val := p.typ(parent) *t = *types.NewChan(dir, val) return t default: errorf("unexpected type tag %d", i) // panics panic("unreachable") } }
// 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 name := p.string() parent = p.pkg() scope := parent.Scope() obj := scope.Lookup(name) // if the object doesn't exist yet, create and insert it if obj == nil { obj = types.NewTypeName(token.NoPos, 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 _, ok := t0.Underlying().(*types.Interface); ok { return t } // read associated methods for i := p.int(); i > 0; i-- { name := p.string() recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? params, isddd := p.paramList() result, _ := p.paramList() p.int() // read and discard index of inlined function body sig := types.NewSignature(recv.At(0), params, result, isddd) t0.AddMethod(types.NewFunc(token.NoPos, parent, name, sig)) } return t case arrayTag: t := new(types.Array) p.record(t) n := p.int64() *t = *types.NewArray(p.typ(parent), n) return t case sliceTag: t := new(types.Slice) p.record(t) *t = *types.NewSlice(p.typ(parent)) return t case dddTag: t := new(dddSlice) p.record(t) t.elem = p.typ(parent) return t case structTag: t := new(types.Struct) p.record(t) n := p.int() fields := make([]*types.Var, n) tags := make([]string, n) for i := range fields { fields[i] = p.field(parent) tags[i] = p.string() } *t = *types.NewStruct(fields, tags) return t case pointerTag: t := new(types.Pointer) p.record(t) *t = *types.NewPointer(p.typ(parent)) return t case signatureTag: t := new(types.Signature) 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) p.record(nil) // no embedded interfaces with gc compiler if p.int() != 0 { panic("unexpected embedded interface") } // read methods methods := make([]*types.Func, p.int()) for i := range methods { pkg, name := p.fieldName(parent) params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) methods[i] = types.NewFunc(token.NoPos, pkg, name, sig) } t := types.NewInterface(methods, nil) p.typList[n] = t return t case mapTag: t := new(types.Map) p.record(t) key := p.typ(parent) val := p.typ(parent) *t = *types.NewMap(key, val) return t case chanTag: t := new(types.Chan) 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)) } }
// funcSig returns the signature of the specified package-level function. func funcSig(pkg *types.Package, name string) *types.Signature { if f, ok := pkg.Scope().Lookup(name).(*types.Func); ok { return f.Type().(*types.Signature) } return nil }