func (g *goGen) typeString(typ types.Type) string { pkg := g.Pkg switch t := typ.(type) { case *types.Named: obj := t.Obj() if obj.Pkg() == nil { // e.g. error type is *types.Named. return types.TypeString(typ, types.RelativeTo(pkg)) } oPkg := obj.Pkg() if !g.validPkg(oPkg) && !isWrapperType(t) { g.errorf("type %s is defined in %s, which is not bound", t, oPkg) return "TODO" } switch t.Underlying().(type) { case *types.Interface, *types.Struct: return fmt.Sprintf("%s%s", g.pkgName(oPkg), types.TypeString(typ, types.RelativeTo(oPkg))) default: g.errorf("unsupported named type %s / %T", t, t) } case *types.Pointer: switch t := t.Elem().(type) { case *types.Named: return fmt.Sprintf("*%s", g.typeString(t)) default: g.errorf("not yet supported, pointer type %s / %T", t, t) } default: return types.TypeString(typ, types.RelativeTo(pkg)) } return "" }
func (g *goGen) typeString(typ types.Type) string { pkg := g.pkg switch t := typ.(type) { case *types.Named: obj := t.Obj() if obj.Pkg() == nil { // e.g. error type is *types.Named. return types.TypeString(typ, types.RelativeTo(pkg)) } if obj.Pkg() != g.pkg { g.errorf("type %s not defined in package %s", t, g.pkg) } switch t.Underlying().(type) { case *types.Interface, *types.Struct: return fmt.Sprintf("%s.%s", pkg.Name(), types.TypeString(typ, types.RelativeTo(pkg))) default: g.errorf("unsupported named type %s / %T", t, t) } case *types.Pointer: switch t := t.Elem().(type) { case *types.Named: return fmt.Sprintf("*%s", g.typeString(t)) default: g.errorf("not yet supported, pointer type %s / %T", t, t) } default: return types.TypeString(typ, types.RelativeTo(pkg)) } return "" }
// writeSignature writes to buf the signature sig in declaration syntax. func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature, params []*Parameter) { buf.WriteString("func ") if recv := sig.Recv(); recv != nil { buf.WriteString("(") if n := params[0].Name(); n != "" { buf.WriteString(n) buf.WriteString(" ") } types.WriteType(buf, params[0].Type(), types.RelativeTo(from)) buf.WriteString(") ") } buf.WriteString(name) types.WriteSignature(buf, sig, types.RelativeTo(from)) }
func TestImportedTypes(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 } for _, test := range importedObjectTests { s := strings.Split(test.name, ".") if len(s) != 2 { t.Fatal("inconsistent test data") } importPath := s[0] objName := s[1] pkg, err := Import(imports, importPath) if err != nil { t.Error(err) continue } obj := pkg.Scope().Lookup(objName) if obj == nil { t.Errorf("%s: object not found", test.name) continue } got := types.ObjectString(obj, types.RelativeTo(pkg)) if got != test.want { t.Errorf("%s: got %q; want %q", test.name, got, test.want) } } }
func printFields(printf printfFunc, node ast.Node, fields []describeField) { if len(fields) > 0 { printf(node, "Fields:") } // Align the names and the types (requires two passes). var width int var names []string for _, f := range fields { var buf bytes.Buffer for _, fld := range f.implicits { buf.WriteString(fld.Obj().Name()) buf.WriteByte('.') } buf.WriteString(f.field.Name()) name := buf.String() if n := utf8.RuneCountInString(name); n > width { width = n } names = append(names, name) } for i, f := range fields { // Print the field type relative to the package // in which it was defined, not the query package, printf(f.field, "\t%*s %s", -width, names[i], types.TypeString(f.field.Type(), types.RelativeTo(f.field.Pkg()))) } }
func formatMember(obj types.Object, maxname int) string { qualifier := types.RelativeTo(obj.Pkg()) var buf bytes.Buffer fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name()) switch obj := obj.(type) { case *types.Const: fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), obj.Val().String()) case *types.Func: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) case *types.TypeName: // Abbreviate long aggregate type names. var abbrev string switch t := obj.Type().Underlying().(type) { case *types.Interface: if t.NumMethods() > 1 { abbrev = "interface{...}" } case *types.Struct: if t.NumFields() > 1 { abbrev = "struct{...}" } } if abbrev == "" { fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier)) } else { fmt.Fprintf(&buf, " %s", abbrev) } case *types.Var: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) } return buf.String() }
func printMethods(printf printfFunc, node ast.Node, methods []*types.Selection) { if len(methods) > 0 { printf(node, "Method set:") } for _, meth := range methods { // Print the method type relative to the package // in which it was defined, not the query package, printf(meth.Obj(), "\t%s", types.SelectionString(meth, types.RelativeTo(meth.Obj().Pkg()))) } }
// prettyFunc pretty-prints fn for the user interface. // TODO(adonovan): return HTML so we have more markup freedom. func prettyFunc(this *types.Package, fn *ssa.Function) string { if fn.Parent() != nil { return fmt.Sprintf("%s in %s", types.TypeString(fn.Signature, types.RelativeTo(this)), prettyFunc(this, fn.Parent())) } if fn.Synthetic != "" && fn.Name() == "init" { // (This is the actual initializer, not a declared 'func init'). if fn.Pkg.Pkg == this { return "package initializer" } return fmt.Sprintf("%q package initializer", fn.Pkg.Pkg.Path()) } return fn.RelString(this) }
func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod { qualifier := types.RelativeTo(this) var jmethods []serial.DescribeMethod for _, meth := range methods { var ser serial.DescribeMethod if meth != nil { // may contain nils when called by implements (on a method) ser = serial.DescribeMethod{ Name: types.SelectionString(meth, qualifier), Pos: fset.Position(meth.Obj().Pos()).String(), } } jmethods = append(jmethods, ser) } return jmethods }
func (r *freevarsResult) PrintPlain(printf printfFunc) { if len(r.refs) == 0 { printf(r.qpos, "No free identifiers.") } else { printf(r.qpos, "Free identifiers:") qualifier := types.RelativeTo(r.qpos.info.Pkg) for _, ref := range r.refs { // Avoid printing "type T T". var typstr string if ref.kind != "type" && ref.kind != "label" { typstr = " " + types.TypeString(ref.typ, qualifier) } printf(ref.obj, "%s %s%s", ref.kind, ref.ref, typstr) } } }
func TestIntuitiveMethodSet(t *testing.T) { const source = ` package P type A int func (A) f() func (*A) g() ` fset := token.NewFileSet() f, err := parser.ParseFile(fset, "hello.go", source, 0) if err != nil { t.Fatal(err) } var conf types.Config pkg, err := conf.Check("P", fset, []*ast.File{f}, nil) if err != nil { t.Fatal(err) } qual := types.RelativeTo(pkg) for _, test := range []struct { expr string // type expression want string // intuitive method set }{ {"A", "(A).f (*A).g"}, {"*A", "(*A).f (*A).g"}, {"error", "(error).Error"}, {"*error", ""}, {"struct{A}", "(struct{A}).f (*struct{A}).g"}, {"*struct{A}", "(*struct{A}).f (*struct{A}).g"}, } { tv, err := types.Eval(fset, pkg, 0, test.expr) if err != nil { t.Errorf("Eval(%s) failed: %v", test.expr, err) } var names []string for _, m := range typeutil.IntuitiveMethodSet(tv.Type, nil) { name := fmt.Sprintf("(%s).%s", types.TypeString(m.Recv(), qual), m.Obj().Name()) names = append(names, name) } got := strings.Join(names, " ") if got != test.want { t.Errorf("IntuitiveMethodSet(%s) = %q, want %q", test.expr, got, test.want) } } }
func (r *describePackageResult) display(printf printfFunc) { printf(r.node, "%s", r.description) // Compute max width of name "column". maxname := 0 for _, mem := range r.members { if l := len(mem.obj.Name()); l > maxname { maxname = l } } for _, mem := range r.members { printf(mem.obj, "\t%s", formatMember(mem.obj, maxname)) for _, meth := range mem.methods { printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg))) } } }
func printResult(res *rta.Result, from *types.Package) string { var buf bytes.Buffer writeSorted := func(ss []string) { sort.Strings(ss) for _, s := range ss { fmt.Fprintf(&buf, " %s\n", s) } } buf.WriteString("Dynamic calls\n") var edges []string callgraph.GraphVisitEdges(res.CallGraph, func(e *callgraph.Edge) error { if strings.Contains(e.Description(), "dynamic") { edges = append(edges, fmt.Sprintf("%s --> %s", e.Caller.Func.RelString(from), e.Callee.Func.RelString(from))) } return nil }) writeSorted(edges) buf.WriteString("Reachable functions\n") var reachable []string for f := range res.Reachable { reachable = append(reachable, f.RelString(from)) } writeSorted(reachable) buf.WriteString("Reflect types\n") var rtypes []string res.RuntimeTypes.Iterate(func(key types.Type, value interface{}) { if value == false { // accessible to reflection rtypes = append(rtypes, types.TypeString(key, types.RelativeTo(from))) } }) writeSorted(rtypes) return strings.TrimSpace(buf.String()) }
// WritePackage writes to buf a human-readable summary of p. func WritePackage(buf *bytes.Buffer, p *Package) { fmt.Fprintf(buf, "%s:\n", p) var names []string maxname := 0 for name := range p.Members { if l := len(name); l > maxname { maxname = l } names = append(names, name) } from := p.Pkg sort.Strings(names) for _, name := range names { switch mem := p.Members[name].(type) { case *NamedConst: fmt.Fprintf(buf, " const %-*s %s = %s\n", maxname, name, mem.Name(), mem.Value.RelString(from)) case *Function: fmt.Fprintf(buf, " func %-*s %s\n", maxname, name, relType(mem.Type(), from)) case *Type: fmt.Fprintf(buf, " type %-*s %s\n", maxname, name, relType(mem.Type().Underlying(), from)) for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) { fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from))) } case *Global: fmt.Fprintf(buf, " var %-*s %s\n", maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from)) } } fmt.Fprintf(buf, "\n") }
func TestStdlib(t *testing.T) { if runtime.GOOS == "android" { t.Skipf("incomplete std lib on %s", runtime.GOOS) } if testing.Short() { t.Skip("skipping in short mode; uses tons of memory (golang.org/issue/14113)") } runtime.GC() t0 := time.Now() var memstats runtime.MemStats runtime.ReadMemStats(&memstats) alloc := memstats.Alloc // Load, parse and type-check the program. ctxt := build.Default // copy ctxt.GOPATH = "" // disable GOPATH conf := loader.Config{Build: &ctxt} for _, path := range buildutil.AllPackages(conf.Build) { conf.ImportWithTests(path) } prog, err := conf.Load() if err != nil { t.Fatalf("Load failed: %v", err) } t1 := time.Now() runtime.GC() runtime.ReadMemStats(&memstats) numPkgs := len(prog.AllPackages) if want := 205; numPkgs < want { t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want) } // Dump package members. if false { for pkg := range prog.AllPackages { fmt.Printf("Package %s:\n", pkg.Path()) scope := pkg.Scope() qualifier := types.RelativeTo(pkg) for _, name := range scope.Names() { if ast.IsExported(name) { fmt.Printf("\t%s\n", types.ObjectString(scope.Lookup(name), qualifier)) } } fmt.Println() } } // Check that Test functions for io/ioutil, regexp and // compress/bzip2 are all simultaneously present. // (The apparent cycle formed when augmenting all three of // these packages by their tests was the original motivation // for reporting b/7114.) // // compress/bzip2.TestBitReader in bzip2_test.go imports io/ioutil // io/ioutil.TestTempFile in tempfile_test.go imports regexp // regexp.TestRE2Search in exec_test.go imports compress/bzip2 for _, test := range []struct{ pkg, fn string }{ {"io/ioutil", "TestTempFile"}, {"regexp", "TestRE2Search"}, {"compress/bzip2", "TestBitReader"}, } { info := prog.Imported[test.pkg] if info == nil { t.Errorf("failed to load package %q", test.pkg) continue } obj, _ := info.Pkg.Scope().Lookup(test.fn).(*types.Func) if obj == nil { t.Errorf("package %q has no func %q", test.pkg, test.fn) continue } } // Dump some statistics. // determine line count var lineCount int prog.Fset.Iterate(func(f *token.File) bool { lineCount += f.LineCount() return true }) t.Log("GOMAXPROCS: ", runtime.GOMAXPROCS(0)) t.Log("#Source lines: ", lineCount) t.Log("Load/parse/typecheck: ", t1.Sub(t0)) t.Log("#MB: ", int64(memstats.Alloc-alloc)/1000000) }
func relType(t types.Type, from *types.Package) string { return types.TypeString(t, types.RelativeTo(from)) }
// SelectionString prints selection sel relative to the query position. func (qpos *queryPos) selectionString(sel *types.Selection) string { return types.SelectionString(sel, types.RelativeTo(qpos.info.Pkg)) }
// ObjectString prints object obj relative to the query position. func (qpos *queryPos) objectString(obj types.Object) string { return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg)) }
// TypeString prints type T relative to the query position. func (qpos *queryPos) typeString(T types.Type) string { return types.TypeString(T, types.RelativeTo(qpos.info.Pkg)) }
// ExampleInfo prints various facts recorded by the type checker in a // types.Info struct: definitions of and references to each named object, // and the type, value, and mode of every expression in the package. func ExampleInfo() { // Parse a single source file. const input = ` package fib type S string var a, b, c = len(b), S(c), "hello" func fib(x int) int { if x < 2 { return x } return fib(x-1) - fib(x-2) }` fset := token.NewFileSet() f, err := parser.ParseFile(fset, "fib.go", input, 0) if err != nil { log.Fatal(err) } // Type-check the package. // We create an empty map for each kind of input // we're interested in, and Check populates them. info := types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), } var conf types.Config pkg, err := conf.Check("fib", fset, []*ast.File{f}, &info) if err != nil { log.Fatal(err) } // Print package-level variables in initialization order. fmt.Printf("InitOrder: %v\n\n", info.InitOrder) // For each named object, print the line and // column of its definition and each of its uses. fmt.Println("Defs and Uses of each named object:") usesByObj := make(map[types.Object][]string) for id, obj := range info.Uses { posn := fset.Position(id.Pos()) lineCol := fmt.Sprintf("%d:%d", posn.Line, posn.Column) usesByObj[obj] = append(usesByObj[obj], lineCol) } var items []string for obj, uses := range usesByObj { sort.Strings(uses) item := fmt.Sprintf("%s:\n defined at %s\n used at %s", types.ObjectString(obj, types.RelativeTo(pkg)), fset.Position(obj.Pos()), strings.Join(uses, ", ")) items = append(items, item) } sort.Strings(items) // sort by line:col, in effect fmt.Println(strings.Join(items, "\n")) fmt.Println() fmt.Println("Types and Values of each expression:") items = nil for expr, tv := range info.Types { var buf bytes.Buffer posn := fset.Position(expr.Pos()) tvstr := tv.Type.String() if tv.Value != nil { tvstr += " = " + tv.Value.String() } // line:col | expr | mode : type = value fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s", posn.Line, posn.Column, exprString(fset, expr), mode(tv), tvstr) items = append(items, buf.String()) } sort.Strings(items) fmt.Println(strings.Join(items, "\n")) // Output: // InitOrder: [c = "hello" b = S(c) a = len(b)] // // Defs and Uses of each named object: // builtin len: // defined at - // used at 6:15 // func fib(x int) int: // defined at fib.go:8:6 // used at 12:20, 12:9 // type S string: // defined at fib.go:4:6 // used at 6:23 // type int int: // defined at - // used at 8:12, 8:17 // type string string: // defined at - // used at 4:8 // var b S: // defined at fib.go:6:8 // used at 6:19 // var c string: // defined at fib.go:6:11 // used at 6:25 // var x int: // defined at fib.go:8:10 // used at 10:10, 12:13, 12:24, 9:5 // // Types and Values of each expression: // 4: 8 | string | type : string // 6:15 | len | builtin : func(string) int // 6:15 | len(b) | value : int // 6:19 | b | var : fib.S // 6:23 | S | type : fib.S // 6:23 | S(c) | value : fib.S // 6:25 | c | var : string // 6:29 | "hello" | value : string = "hello" // 8:12 | int | type : int // 8:17 | int | type : int // 9: 5 | x | var : int // 9: 5 | x < 2 | value : untyped bool // 9: 9 | 2 | value : int = 2 // 10:10 | x | var : int // 12: 9 | fib | value : func(x int) int // 12: 9 | fib(x - 1) | value : int // 12: 9 | fib(x-1) - fib(x-2) | value : int // 12:13 | x | var : int // 12:13 | x - 1 | value : int // 12:15 | 1 | value : int = 1 // 12:20 | fib | value : func(x int) int // 12:20 | fib(x - 2) | value : int // 12:24 | x | var : int // 12:24 | x - 2 | value : int // 12:26 | 2 | value : int = 2 }
func (sym *symtab) typename(t types.Type, pkg *types.Package) string { if pkg == nil { return types.TypeString(t, nil) } return types.TypeString(t, types.RelativeTo(pkg)) }
func (r *referrersInitialResult) PrintPlain(printf printfFunc) { printf(r.obj, "references to %s", types.ObjectString(r.obj, types.RelativeTo(r.qinfo.Pkg))) }
func (a *analysis) namedType(obj *types.TypeName, implements map[*types.Named]implementsFacts) { qualifier := types.RelativeTo(obj.Pkg()) T := obj.Type().(*types.Named) v := &TypeInfoJSON{ Name: obj.Name(), Size: sizes.Sizeof(T), Align: sizes.Alignof(T), Methods: []anchorJSON{}, // (JS wants non-nil) } // addFact adds the fact "is implemented by T" (by) or // "implements T" (!by) to group. addFact := func(group *implGroupJSON, T types.Type, by bool) { Tobj := deref(T).(*types.Named).Obj() var byKind string if by { // Show underlying kind of implementing type, // e.g. "slice", "array", "struct". s := reflect.TypeOf(T.Underlying()).String() byKind = strings.ToLower(strings.TrimPrefix(s, "*types.")) } group.Facts = append(group.Facts, implFactJSON{ ByKind: byKind, Other: anchorJSON{ Href: a.posURL(Tobj.Pos(), len(Tobj.Name())), Text: types.TypeString(T, qualifier), }, }) } // IMPLEMENTS if r, ok := implements[T]; ok { if isInterface(T) { // "T is implemented by <conc>" ... // "T is implemented by <iface>"... // "T implements <iface>"... group := implGroupJSON{ Descr: types.TypeString(T, qualifier), } // Show concrete types first; use two passes. for _, sub := range r.to { if !isInterface(sub) { addFact(&group, sub, true) } } for _, sub := range r.to { if isInterface(sub) { addFact(&group, sub, true) } } for _, super := range r.from { addFact(&group, super, false) } v.ImplGroups = append(v.ImplGroups, group) } else { // T is concrete. if r.from != nil { // "T implements <iface>"... group := implGroupJSON{ Descr: types.TypeString(T, qualifier), } for _, super := range r.from { addFact(&group, super, false) } v.ImplGroups = append(v.ImplGroups, group) } if r.fromPtr != nil { // "*C implements <iface>"... group := implGroupJSON{ Descr: "*" + types.TypeString(T, qualifier), } for _, psuper := range r.fromPtr { addFact(&group, psuper, false) } v.ImplGroups = append(v.ImplGroups, group) } } } // METHOD SETS for _, sel := range typeutil.IntuitiveMethodSet(T, &a.prog.MethodSets) { meth := sel.Obj().(*types.Func) pos := meth.Pos() // may be 0 for error.Error v.Methods = append(v.Methods, anchorJSON{ Href: a.posURL(pos, len(meth.Name())), Text: types.SelectionString(sel, qualifier), }) } // Since there can be many specs per decl, we // can't attach the link to the keyword 'type' // (as we do with 'func'); we use the Ident. fi, offset := a.fileAndOffset(obj.Pos()) fi.addLink(aLink{ start: offset, end: offset + len(obj.Name()), title: fmt.Sprintf("type info for %s", obj.Name()), onclick: fmt.Sprintf("onClickTypeInfo(%d)", fi.addData(v)), }) // Add info for exported package-level types to the package info. if obj.Exported() && isPackageLevel(obj) { // TODO(adonovan): Path is not unique! // It is possible to declare a non-test package called x_test. a.result.pkgInfo(obj.Pkg().Path()).addType(v) } }
func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]InitData, test *importerTest) { pkg, err := imp(make(map[string]*types.Package), test.pkgpath) if err != nil { t.Error(err) return } if test.name != "" { obj := pkg.Scope().Lookup(test.name) if obj == nil { t.Errorf("%s: object not found", test.name) return } got := types.ObjectString(obj, types.RelativeTo(pkg)) if got != test.want { t.Errorf("%s: got %q; want %q", test.name, got, test.want) } if test.wantval != "" { gotval := obj.(*types.Const).Val().String() if gotval != test.wantval { t.Errorf("%s: got val %q; want val %q", test.name, gotval, test.wantval) } } } if len(test.wantinits) > 0 { initdata := initmap[pkg] found := false // Check that the package's own init function has the package's priority for _, pkginit := range initdata.Inits { if pkginit.InitFunc == test.wantinits[0] { if initdata.Priority != pkginit.Priority { t.Errorf("%s: got self priority %d; want %d", test.pkgpath, pkginit.Priority, initdata.Priority) } found = true break } } if !found { t.Errorf("%s: could not find expected function %q", test.pkgpath, test.wantinits[0]) } // Each init function in the list other than the first one is a // dependency of the function immediately before it. Check that // the init functions appear in descending priority order. priority := initdata.Priority for _, wantdepinit := range test.wantinits[1:] { found = false for _, pkginit := range initdata.Inits { if pkginit.InitFunc == wantdepinit { if priority <= pkginit.Priority { t.Errorf("%s: got dep priority %d; want less than %d", test.pkgpath, pkginit.Priority, priority) } found = true priority = pkginit.Priority break } } if !found { t.Errorf("%s: could not find expected function %q", test.pkgpath, wantdepinit) } } } }
func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Named]implementsFacts) { // We must not assume the corresponding SSA packages were // created (i.e. were transitively error-free). // IMPORTS for _, f := range info.Files { // Package decl. fi, offset := a.fileAndOffset(f.Name.Pos()) fi.addLink(aLink{ start: offset, end: offset + len(f.Name.Name), title: "Package docs for " + info.Pkg.Path(), // TODO(adonovan): fix: we're putting the untrusted Path() // into a trusted field. What's the appropriate sanitizer? href: "/pkg/" + info.Pkg.Path(), }) // Import specs. for _, imp := range f.Imports { // Remove quotes. L := int(imp.End()-imp.Path.Pos()) - len(`""`) path, _ := strconv.Unquote(imp.Path.Value) fi, offset := a.fileAndOffset(imp.Path.Pos()) fi.addLink(aLink{ start: offset + 1, end: offset + 1 + L, title: "Package docs for " + path, // TODO(adonovan): fix: we're putting the untrusted path // into a trusted field. What's the appropriate sanitizer? href: "/pkg/" + path, }) } } // RESOLUTION qualifier := types.RelativeTo(info.Pkg) for id, obj := range info.Uses { // Position of the object definition. pos := obj.Pos() Len := len(obj.Name()) // Correct the position for non-renaming import specs. // import "sync/atomic" // ^^^^^^^^^^^ if obj, ok := obj.(*types.PkgName); ok && id.Name == obj.Imported().Name() { // Assume this is a non-renaming import. // NB: not true for degenerate renamings: `import foo "foo"`. pos++ Len = len(obj.Imported().Path()) } if obj.Pkg() == nil { continue // don't mark up built-ins. } fi, offset := a.fileAndOffset(id.NamePos) fi.addLink(aLink{ start: offset, end: offset + len(id.Name), title: types.ObjectString(obj, qualifier), href: a.posURL(pos, Len), }) } // IMPLEMENTS & METHOD SETS for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok { a.namedType(obj, implements) } } }