// 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 (w *PkgWalker) LookupStructFromField(info *types.Info, cursorPkg *types.Package, cursorObj types.Object, cursorPos token.Pos) types.Object { if info == nil { conf := &PkgConfig{ IgnoreFuncBodies: true, AllowBinary: true, WithTestFiles: true, Info: &types.Info{ Defs: make(map[*ast.Ident]types.Object), }, } w.imported[cursorPkg.Path()] = nil pkg, _ := w.Import("", cursorPkg.Path(), conf) if pkg != nil { info = conf.Info } } for _, obj := range info.Defs { if obj == nil { continue } if _, ok := obj.(*types.TypeName); ok { if t, ok := obj.Type().Underlying().(*types.Struct); ok { for i := 0; i < t.NumFields(); i++ { if t.Field(i).Pos() == cursorPos { return obj } } } } } return nil }
func FindAllExports(pkg *types.Package, fset *token.FileSet) []UnexportCandidate { candidates := []UnexportCandidate{} for _, name := range pkg.Scope().Names() { obj := pkg.Scope().Lookup(name) if !obj.Exported() { continue } displayName := obj.Name() if _, ok := obj.(*types.Func); ok { displayName += "()" } candidate := UnexportCandidate{obj.Name(), displayName, fset.Position(obj.Pos())} candidates = append(candidates, candidate) if tn, ok := obj.(*types.TypeName); ok { if str, ok := tn.Type().Underlying().(*types.Struct); ok { candidates = append(candidates, findStructFields(str, obj.Name(), fset)...) } ptrType := types.NewPointer(tn.Type()) methodSet := types.NewMethodSet(ptrType) for i := 0; i < methodSet.Len(); i++ { methodSel := methodSet.At(i) method := methodSel.Obj() // skip unexported functions, and functions from embedded fields. // The best I can figure out for embedded functions is if the selection index path is longer than 1. if !method.Exported() || len(methodSel.Index()) > 1 { continue } candidate := UnexportCandidate{method.Name(), obj.Name() + "." + method.Name() + "()", fset.Position(method.Pos())} candidates = append(candidates, candidate) } } } return candidates }
func IsSamePkg(a, b *types.Package) bool { if a == b { return true } if a == nil || b == nil { return false } return a.Path() == b.Path() }
func (w *PkgWalker) LookupImport(pkg *types.Package, pkgInfo *types.Info, cursor *FileCursor, is *ast.ImportSpec) { fpath, err := strconv.Unquote(is.Path.Value) if err != nil { return } if typesFindDef { fmt.Println(w.fset.Position(is.Pos())) } fbase := fpath pos := strings.LastIndexAny(fpath, "./-\\") if pos != -1 { fbase = fpath[pos+1:] } var fname string if is.Name != nil { fname = is.Name.Name } else { fname = fbase } if typesFindInfo { if fname == fpath { fmt.Printf("package %s\n", fname) } else { fmt.Printf("package %s (\"%s\")\n", fname, fpath) } } if !typesFindUse { return } path := pkg.Path() if strings.Contains(path, "vendor/") { path = strings.Split(path, "vendor/")[1] } fid := path + "." + fname var usages []int for id, obj := range pkgInfo.Uses { if obj != nil && obj.Id() == fid { //!= nil && cursorObj.Pos() == obj.Pos() { usages = append(usages, int(id.Pos())) } } (sort.IntSlice(usages)).Sort() for _, pos := range usages { fmt.Println(w.fset.Position(token.Pos(pos))) } }
func (c *funcContext) pkgVar(pkg *types.Package) string { if pkg == c.p.Pkg { return "$pkg" } pkgVar, found := c.p.pkgVars[pkg.Path()] if !found { pkgVar = fmt.Sprintf(`$packages["%s"]`, pkg.Path()) } return pkgVar }
// pkgString returns a string representation of a package's exported interface. func pkgString(pkg *types.Package) string { var buf bytes.Buffer fmt.Fprintf(&buf, "package %s\n", pkg.Name()) scope := pkg.Scope() for _, name := range scope.Names() { if exported(name) { obj := scope.Lookup(name) buf.WriteString(obj.String()) switch obj := obj.(type) { case *types.Const: // For now only print constant values if they are not float // or complex. This permits comparing go/types results with // gc-generated gcimported package interfaces. info := obj.Type().Underlying().(*types.Basic).Info() if info&types.IsFloat == 0 && info&types.IsComplex == 0 { fmt.Fprintf(&buf, " = %s", obj.Val()) } case *types.TypeName: // Print associated methods. // Basic types (e.g., unsafe.Pointer) have *types.Basic // type rather than *types.Named; so we need to check. if typ, _ := obj.Type().(*types.Named); typ != nil { if n := typ.NumMethods(); n > 0 { // Sort methods by name so that we get the // same order independent of whether the // methods got imported or coming directly // for the source. // TODO(gri) This should probably be done // in go/types. list := make([]*types.Func, n) for i := 0; i < n; i++ { list[i] = typ.Method(i) } sort.Sort(byName(list)) buf.WriteString("\nmethods (\n") for _, m := range list { fmt.Fprintf(&buf, "\t%s\n", m) } buf.WriteString(")") } } } buf.WriteByte('\n') } } return buf.String() }
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 }
func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool { // Reject cross-package references if r.to is unexported. // (Such references may be qualified identifiers or field/method // selections.) if !ast.IsExported(r.to) && pkg != from.Pkg() { r.errorf(from.Pos(), "renaming this %s %q to %q would make it unexported", objectKind(from), from.Name(), r.to) r.errorf(id.Pos(), "\tbreaking references from packages such as %q", pkg.Path()) return false } return true }
func walkPkg(typpkg *types.Package, docpkg *doc.Package, f func(*types.Struct, *types.TypeName, *doc.Package)) { for _, name := range typpkg.Scope().Names() { obj := typpkg.Scope().Lookup(name) if typename, ok := obj.(*types.TypeName); ok { named := typename.Type().(*types.Named) if strukt, ok := named.Underlying().(*types.Struct); ok && strukt.NumFields() > 0 && strukt.Field(0).Name() == "TypeMeta" { if len(os.Args) == 3 || os.Args[3] == typename.Name() { f(strukt, typename, docpkg) } } } } }
// BuildPackage builds an SSA program with IR for a single package. // // It populates pkg by type-checking the specified file ASTs. All // dependencies are loaded using the importer specified by tc, which // typically loads compiler export data; SSA code cannot be built for // those packages. BuildPackage then constructs an ssa.Program with all // dependency packages created, and builds and returns the SSA package // corresponding to pkg. // // The caller must have set pkg.Path() to the import path. // // The operation fails if there were any type-checking or import errors. // // See ../ssa/example_test.go for an example. // func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) { if fset == nil { panic("no token.FileSet") } if pkg.Path() == "" { panic("package has no import path") } 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), } if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil { return nil, nil, err } prog := ssa.NewProgram(fset, mode) // Create SSA packages for all imports. // Order is not significant. created := make(map[*types.Package]bool) var createAll func(pkgs []*types.Package) createAll = func(pkgs []*types.Package) { for _, p := range pkgs { if !created[p] { created[p] = true prog.CreatePackage(p, nil, nil, true) createAll(p.Imports()) } } } createAll(pkg.Imports()) // Create and build the primary package. ssapkg := prog.CreatePackage(pkg, files, info, false) ssapkg.Build() return ssapkg, info, nil }
func defaultFileName(lang string, pkg *types.Package) string { if *outdir == "" { return "" } switch lang { case "java": firstRune, size := utf8.DecodeRuneInString(pkg.Name()) className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:] return filepath.Join(*outdir, className+".java") case "go": return filepath.Join(*outdir, "go_"+pkg.Name()+".go") case "objc": firstRune, size := utf8.DecodeRuneInString(pkg.Name()) className := string(unicode.ToUpper(firstRune)) + pkg.Name()[size:] return filepath.Join(*outdir, "Go"+className+".m") } errorf("unknown target language: %q", lang) os.Exit(exitStatus) return "" }
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())) } }
//!+ 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 (p *exporter) pkg(pkg *types.Package) { if trace { p.tracef("package { ") defer p.tracef("} ") } if pkg == nil { panic("unexpected nil pkg") } // if the package was seen before, write its index (>= 0) if i, ok := p.pkgIndex[pkg]; ok { p.int(i) return } p.pkgIndex[pkg] = len(p.pkgIndex) // otherwise, write the package tag (< 0) and package data p.int(packageTag) p.string(pkg.Name()) p.string(pkg.Path()) }
func findFromObjects(iprog *loader.Program, spec *spec) ([]types.Object, error) { if spec.filename != "" { return findFromObjectsInFile(iprog, spec) } // Search for objects defined in specified package. // TODO(adonovan): the iprog.ImportMap has an entry {"main": ...} // for main packages, even though that's not an import path. // Seems like a bug. // // pkg := iprog.ImportMap[spec.pkg] // if pkg == nil { // return fmt.Errorf("cannot find package %s", spec.pkg) // can't happen? // } // info := iprog.AllPackages[pkg] // Workaround: lookup by value. var info *loader.PackageInfo var pkg *types.Package for pkg, info = range iprog.AllPackages { if pkg.Path() == spec.pkg { break } } if info == nil { return nil, fmt.Errorf("package %q was not loaded", spec.pkg) } objects, err := findObjects(info, spec) if err != nil { return nil, err } if len(objects) > 1 { // ambiguous "*" scope query return nil, ambiguityError(iprog.Fset, objects) } return objects, nil }
// 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 }
// Structure computes the structure of the lexical environment of the // package specified by (pkg, info, files). // // The info.{Types,Defs,Uses,Implicits} maps must have been populated // by the type-checker // // fset is used for logging. // func Structure(fset *token.FileSet, pkg *types.Package, info *types.Info, files []*ast.File) *Info { r := resolver{ fset: fset, imports: make(map[string]*types.Package), result: &Info{ Defs: make(map[types.Object]*Block), Refs: make(map[types.Object][]Reference), Blocks: make(map[ast.Node]*Block), }, pkg: pkg, info: info, } // Build import map for just this package. r.imports["unsafe"] = types.Unsafe for _, imp := range pkg.Imports() { r.imports[imp.Path()] = imp } r.doPackage(pkg, files) return r.result }
func (p *Processor) processPackage(pkg *Package, typesPkg *types.Package) { pkg.Models = make([]*Model, 0) pkg.Structs = make([]string, 0) pkg.Functions = make([]string, 0) s := typesPkg.Scope() for _, name := range s.Names() { fun := p.tryGetFunction(s.Lookup(name)) if fun != nil { pkg.Functions = append(pkg.Functions, name) } str := p.tryGetStruct(s.Lookup(name).Type()) if str == nil { continue } if m := p.processStruct(name, str); m != nil { pkg.Models = append(pkg.Models, m) } else { pkg.Structs = append(pkg.Structs, name) } } }
// NewPackageDef creates a new Def that represents a Go package. func (g *Grapher) NewPackageDef(pkgInfo *loader.PackageInfo, pkg *types.Package) (*Def, error) { var pkgDir string if len(pkgInfo.Files) > 0 { pkgDir = filepath.Dir(g.program.Fset.Position(pkgInfo.Files[0].Package).Filename) } return &Def{ Name: pkg.Name(), DefKey: &DefKey{PackageImportPath: pkg.Path(), Path: []string{}}, File: pkgDir, DefInfo: definfo.DefInfo{ Exported: true, PkgName: pkg.Name(), Kind: definfo.Package, }, }, nil }
// 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 }
func (r *resolver) qualifier(pkg *types.Package) string { if pkg == r.pkg { return "" // unqualified intra-package reference } return pkg.Path() }
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") }
func testExportImport(t *testing.T, pkg0 *types.Package, path string) (size, gcsize int) { data := ExportData(pkg0) size = len(data) imports := make(map[string]*types.Package) n, pkg1, err := ImportData(imports, data) if err != nil { t.Errorf("package %s: import failed: %s", pkg0.Name(), err) return } if n != size { t.Errorf("package %s: not all input data consumed", pkg0.Name()) return } s0 := pkgString(pkg0) s1 := pkgString(pkg1) if s1 != s0 { t.Errorf("package %s: \nimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s1, s0) } // If we have a standard library, compare also against the gcimported package. if path == "" { return // not std library } gcdata, err := gcExportData(path) if err != nil { if pkg0.Name() == "main" { return // no export data present for main package } t.Errorf("package %s: couldn't get export data: %s", pkg0.Name(), err) } gcsize = len(gcdata) imports = make(map[string]*types.Package) pkg2, err := gcImportData(imports, gcdata, path) if err != nil { t.Errorf("package %s: gcimport failed: %s", pkg0.Name(), err) return } s2 := pkgString(pkg2) if s2 != s0 { t.Errorf("package %s: \ngcimport got:\n%s\nwant:\n%s\n", pkg0.Name(), s2, s0) } return }
func (p *Processor) ProcessTypesPkg(typesPkg *types.Package) (*Package, error) { pkg := &Package{Name: typesPkg.Name()} p.processPackage(pkg, typesPkg) return pkg, nil }
func (sym *symtab) addType(obj types.Object, t types.Type) { fn := sym.typename(t, nil) n := sym.typename(t, sym.pkg) var pkg *types.Package if obj != nil { pkg = obj.Pkg() } id := n if pkg != nil { id = pkg.Name() + "_" + n } kind := skType switch typ := t.(type) { case *types.Basic: kind |= skBasic styp := sym.symtype(typ) if styp == nil { panic(fmt.Errorf("builtin type not already known [%s]!", n)) } case *types.Array: sym.addArrayType(pkg, obj, t, kind, id, n) case *types.Slice: sym.addSliceType(pkg, obj, t, kind, id, n) case *types.Signature: sym.addSignatureType(pkg, obj, t, kind, id, n) case *types.Named: kind |= skNamed switch typ := typ.Underlying().(type) { case *types.Struct: sym.addStructType(pkg, obj, t, kind, id, n) case *types.Basic: bsym := sym.symtype(typ) sym.syms[fn] = &symbol{ gopkg: pkg, goobj: obj, gotyp: t, kind: kind | skBasic, id: id, goname: n, cgoname: "cgo_type_" + id, cpyname: "cpy_type_" + id, pyfmt: bsym.pyfmt, pybuf: bsym.pybuf, pysig: "object", c2py: "cgopy_cnv_c2py_" + id, py2c: "cgopy_cnv_py2c_" + id, pychk: fmt.Sprintf("cpy_func_%[1]s_check(%%s)", id), } case *types.Array: sym.addArrayType(pkg, obj, t, kind, id, n) case *types.Slice: sym.addSliceType(pkg, obj, t, kind, id, n) case *types.Signature: sym.addSignatureType(pkg, obj, t, kind, id, n) case *types.Pointer: sym.addPointerType(pkg, obj, t, kind, id, n) case *types.Interface: sym.addInterfaceType(pkg, obj, t, kind, id, n) default: panic(fmt.Errorf("unhandled named-type: [%T]\n%#v\n", obj, t)) } // add methods for i := 0; i < typ.NumMethods(); i++ { m := typ.Method(i) if !m.Exported() { continue } if true { mid := id + "_" + m.Name() mname := m.Name() sym.addMethod(pkg, m, m.Type(), skFunc, mid, mname) } } case *types.Pointer: sym.addPointerType(pkg, obj, t, kind, id, n) case *types.Map: sym.addMapType(pkg, obj, t, kind, id, n) default: panic(fmt.Errorf("unhandled obj [%T]\ntype [%#v]", obj, t)) } }
// FunctionType = ParamList ResultList . func (p *parser) parseFunctionType(pkg *types.Package) *types.Signature { params, isVariadic := p.parseParamList(pkg) results := p.parseResultList(pkg) return types.NewSignature(pkg.Scope(), nil, params, results, isVariadic) }
// Transform applies the transformation to the specified parsed file, // whose type information is supplied in info, and returns the number // of replacements that were made. // // It mutates the AST in place (the identity of the root node is // unchanged), and may add nodes for which no type information is // available in info. // // Derived from rewriteFile in $GOROOT/src/cmd/gofmt/rewrite.go. // func (tr *Transformer) Transform(info *types.Info, pkg *types.Package, file *ast.File) int { if !tr.seenInfos[info] { tr.seenInfos[info] = true mergeTypeInfo(&tr.info.Info, info) } tr.currentPkg = pkg tr.nsubsts = 0 if tr.verbose { fmt.Fprintf(os.Stderr, "before: %s\n", astString(tr.fset, tr.before)) fmt.Fprintf(os.Stderr, "after: %s\n", astString(tr.fset, tr.after)) } var f func(rv reflect.Value) reflect.Value f = func(rv reflect.Value) reflect.Value { // don't bother if val is invalid to start with if !rv.IsValid() { return reflect.Value{} } rv = apply(f, rv) e := rvToExpr(rv) if e != nil { savedEnv := tr.env tr.env = make(map[string]ast.Expr) // inefficient! Use a slice of k/v pairs if tr.matchExpr(tr.before, e) { if tr.verbose { fmt.Fprintf(os.Stderr, "%s matches %s", astString(tr.fset, tr.before), astString(tr.fset, e)) if len(tr.env) > 0 { fmt.Fprintf(os.Stderr, " with:") for name, ast := range tr.env { fmt.Fprintf(os.Stderr, " %s->%s", name, astString(tr.fset, ast)) } } fmt.Fprintf(os.Stderr, "\n") } tr.nsubsts++ // Clone the replacement tree, performing parameter substitution. // We update all positions to n.Pos() to aid comment placement. rv = tr.subst(tr.env, reflect.ValueOf(tr.after), reflect.ValueOf(e.Pos())) } tr.env = savedEnv } return rv } file2 := apply(f, reflect.ValueOf(file)).Interface().(*ast.File) // By construction, the root node is unchanged. if file != file2 { panic("BUG") } // Add any necessary imports. // TODO(adonovan): remove no-longer needed imports too. if tr.nsubsts > 0 { pkgs := make(map[string]*types.Package) for obj := range tr.importedObjs { pkgs[obj.Pkg().Path()] = obj.Pkg() } for _, imp := range file.Imports { path, _ := strconv.Unquote(imp.Path.Value) delete(pkgs, path) } delete(pkgs, pkg.Path()) // don't import self // NB: AddImport may completely replace the AST! // It thus renders info and tr.info no longer relevant to file. var paths []string for path := range pkgs { paths = append(paths, path) } sort.Strings(paths) for _, path := range paths { astutil.AddImport(tr.fset, file, path) } } tr.currentPkg = nil return tr.nsubsts }
func describePackage(o *Oracle, qpos *QueryPos, path []ast.Node) (*describePackageResult, error) { var description string var pkg *types.Package switch n := path[0].(type) { case *ast.ImportSpec: var pkgname *types.PkgName if n.Name != nil { pkgname = qpos.info.Defs[n.Name].(*types.PkgName) } else if p := qpos.info.Implicits[n]; p != nil { pkgname = p.(*types.PkgName) } 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{o.fset, path[0], description, pkg, members}, nil }
func (o *Object) setPkg(p *types.Package) { if p != nil { o.PkgPath = p.Path() o.PkgName = p.Name() } }