github.com/v2fly/tools@v0.100.0/godoc/analysis/typeinfo.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package analysis 6 7 // This file computes the markup for information from go/types: 8 // IMPORTS, identifier RESOLUTION, METHOD SETS, size/alignment, and 9 // the IMPLEMENTS relation. 10 // 11 // IMPORTS links connect import specs to the documentation for the 12 // imported package. 13 // 14 // RESOLUTION links referring identifiers to their defining 15 // identifier, and adds tooltips for kind and type. 16 // 17 // METHOD SETS, size/alignment, and the IMPLEMENTS relation are 18 // displayed in the lower pane when a type's defining identifier is 19 // clicked. 20 21 import ( 22 "fmt" 23 "go/types" 24 "reflect" 25 "strconv" 26 "strings" 27 28 "github.com/v2fly/tools/go/loader" 29 "github.com/v2fly/tools/go/types/typeutil" 30 ) 31 32 // TODO(adonovan): audit to make sure it's safe on ill-typed packages. 33 34 // TODO(adonovan): use same Sizes as loader.Config. 35 var sizes = types.StdSizes{WordSize: 8, MaxAlign: 8} 36 37 func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Named]implementsFacts) { 38 // We must not assume the corresponding SSA packages were 39 // created (i.e. were transitively error-free). 40 41 // IMPORTS 42 for _, f := range info.Files { 43 // Package decl. 44 fi, offset := a.fileAndOffset(f.Name.Pos()) 45 fi.addLink(aLink{ 46 start: offset, 47 end: offset + len(f.Name.Name), 48 title: "Package docs for " + info.Pkg.Path(), 49 // TODO(adonovan): fix: we're putting the untrusted Path() 50 // into a trusted field. What's the appropriate sanitizer? 51 href: "/pkg/" + info.Pkg.Path(), 52 }) 53 54 // Import specs. 55 for _, imp := range f.Imports { 56 // Remove quotes. 57 L := int(imp.End()-imp.Path.Pos()) - len(`""`) 58 path, _ := strconv.Unquote(imp.Path.Value) 59 fi, offset := a.fileAndOffset(imp.Path.Pos()) 60 fi.addLink(aLink{ 61 start: offset + 1, 62 end: offset + 1 + L, 63 title: "Package docs for " + path, 64 // TODO(adonovan): fix: we're putting the untrusted path 65 // into a trusted field. What's the appropriate sanitizer? 66 href: "/pkg/" + path, 67 }) 68 } 69 } 70 71 // RESOLUTION 72 qualifier := types.RelativeTo(info.Pkg) 73 for id, obj := range info.Uses { 74 // Position of the object definition. 75 pos := obj.Pos() 76 Len := len(obj.Name()) 77 78 // Correct the position for non-renaming import specs. 79 // import "sync/atomic" 80 // ^^^^^^^^^^^ 81 if obj, ok := obj.(*types.PkgName); ok && id.Name == obj.Imported().Name() { 82 // Assume this is a non-renaming import. 83 // NB: not true for degenerate renamings: `import foo "foo"`. 84 pos++ 85 Len = len(obj.Imported().Path()) 86 } 87 88 if obj.Pkg() == nil { 89 continue // don't mark up built-ins. 90 } 91 92 fi, offset := a.fileAndOffset(id.NamePos) 93 fi.addLink(aLink{ 94 start: offset, 95 end: offset + len(id.Name), 96 title: types.ObjectString(obj, qualifier), 97 href: a.posURL(pos, Len), 98 }) 99 } 100 101 // IMPLEMENTS & METHOD SETS 102 for _, obj := range info.Defs { 103 if obj, ok := obj.(*types.TypeName); ok { 104 if named, ok := obj.Type().(*types.Named); ok { 105 a.namedType(named, implements) 106 } 107 } 108 } 109 } 110 111 func (a *analysis) namedType(T *types.Named, implements map[*types.Named]implementsFacts) { 112 obj := T.Obj() 113 qualifier := types.RelativeTo(obj.Pkg()) 114 v := &TypeInfoJSON{ 115 Name: obj.Name(), 116 Size: sizes.Sizeof(T), 117 Align: sizes.Alignof(T), 118 Methods: []anchorJSON{}, // (JS wants non-nil) 119 } 120 121 // addFact adds the fact "is implemented by T" (by) or 122 // "implements T" (!by) to group. 123 addFact := func(group *implGroupJSON, T types.Type, by bool) { 124 Tobj := deref(T).(*types.Named).Obj() 125 var byKind string 126 if by { 127 // Show underlying kind of implementing type, 128 // e.g. "slice", "array", "struct". 129 s := reflect.TypeOf(T.Underlying()).String() 130 byKind = strings.ToLower(strings.TrimPrefix(s, "*types.")) 131 } 132 group.Facts = append(group.Facts, implFactJSON{ 133 ByKind: byKind, 134 Other: anchorJSON{ 135 Href: a.posURL(Tobj.Pos(), len(Tobj.Name())), 136 Text: types.TypeString(T, qualifier), 137 }, 138 }) 139 } 140 141 // IMPLEMENTS 142 if r, ok := implements[T]; ok { 143 if isInterface(T) { 144 // "T is implemented by <conc>" ... 145 // "T is implemented by <iface>"... 146 // "T implements <iface>"... 147 group := implGroupJSON{ 148 Descr: types.TypeString(T, qualifier), 149 } 150 // Show concrete types first; use two passes. 151 for _, sub := range r.to { 152 if !isInterface(sub) { 153 addFact(&group, sub, true) 154 } 155 } 156 for _, sub := range r.to { 157 if isInterface(sub) { 158 addFact(&group, sub, true) 159 } 160 } 161 for _, super := range r.from { 162 addFact(&group, super, false) 163 } 164 v.ImplGroups = append(v.ImplGroups, group) 165 } else { 166 // T is concrete. 167 if r.from != nil { 168 // "T implements <iface>"... 169 group := implGroupJSON{ 170 Descr: types.TypeString(T, qualifier), 171 } 172 for _, super := range r.from { 173 addFact(&group, super, false) 174 } 175 v.ImplGroups = append(v.ImplGroups, group) 176 } 177 if r.fromPtr != nil { 178 // "*C implements <iface>"... 179 group := implGroupJSON{ 180 Descr: "*" + types.TypeString(T, qualifier), 181 } 182 for _, psuper := range r.fromPtr { 183 addFact(&group, psuper, false) 184 } 185 v.ImplGroups = append(v.ImplGroups, group) 186 } 187 } 188 } 189 190 // METHOD SETS 191 for _, sel := range typeutil.IntuitiveMethodSet(T, &a.prog.MethodSets) { 192 meth := sel.Obj().(*types.Func) 193 pos := meth.Pos() // may be 0 for error.Error 194 v.Methods = append(v.Methods, anchorJSON{ 195 Href: a.posURL(pos, len(meth.Name())), 196 Text: types.SelectionString(sel, qualifier), 197 }) 198 } 199 200 // Since there can be many specs per decl, we 201 // can't attach the link to the keyword 'type' 202 // (as we do with 'func'); we use the Ident. 203 fi, offset := a.fileAndOffset(obj.Pos()) 204 fi.addLink(aLink{ 205 start: offset, 206 end: offset + len(obj.Name()), 207 title: fmt.Sprintf("type info for %s", obj.Name()), 208 onclick: fmt.Sprintf("onClickTypeInfo(%d)", fi.addData(v)), 209 }) 210 211 // Add info for exported package-level types to the package info. 212 if obj.Exported() && isPackageLevel(obj) { 213 // TODO(adonovan): Path is not unique! 214 // It is possible to declare a non-test package called x_test. 215 a.result.pkgInfo(obj.Pkg().Path()).addType(v) 216 } 217 } 218 219 // -- utilities -------------------------------------------------------- 220 221 func isInterface(T types.Type) bool { return types.IsInterface(T) } 222 223 // deref returns a pointer's element type; otherwise it returns typ. 224 func deref(typ types.Type) types.Type { 225 if p, ok := typ.Underlying().(*types.Pointer); ok { 226 return p.Elem() 227 } 228 return typ 229 } 230 231 // isPackageLevel reports whether obj is a package-level object. 232 func isPackageLevel(obj types.Object) bool { 233 return obj.Pkg().Scope().Lookup(obj.Name()) == obj 234 }