github.com/april1989/origin-go-tools@v0.0.32/cmd/guru/implements.go (about) 1 // Copyright 2013 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 main 6 7 import ( 8 "fmt" 9 "go/ast" 10 "go/token" 11 "go/types" 12 "reflect" 13 "sort" 14 "strings" 15 16 "github.com/april1989/origin-go-tools/cmd/guru/serial" 17 "github.com/april1989/origin-go-tools/go/loader" 18 "github.com/april1989/origin-go-tools/go/types/typeutil" 19 "github.com/april1989/origin-go-tools/refactor/importgraph" 20 ) 21 22 // The implements function displays the "implements" relation as it pertains to the 23 // selected type. 24 // If the selection is a method, 'implements' displays 25 // the corresponding methods of the types that would have been reported 26 // by an implements query on the receiver type. 27 // 28 func implements(q *Query) error { 29 lconf := loader.Config{Build: q.Build} 30 allowErrors(&lconf) 31 32 qpkg, err := importQueryPackage(q.Pos, &lconf) 33 if err != nil { 34 return err 35 } 36 37 // Set the packages to search. 38 if len(q.Scope) > 0 { 39 // Inspect all packages in the analysis scope, if specified. 40 if err := setPTAScope(&lconf, q.Scope); err != nil { 41 return err 42 } 43 } else { 44 // Otherwise inspect the forward and reverse 45 // transitive closure of the selected package. 46 // (In theory even this is incomplete.) 47 _, rev, _ := importgraph.Build(q.Build) 48 for path := range rev.Search(qpkg) { 49 lconf.ImportWithTests(path) 50 } 51 52 // TODO(adonovan): for completeness, we should also 53 // type-check and inspect function bodies in all 54 // imported packages. This would be expensive, but we 55 // could optimize by skipping functions that do not 56 // contain type declarations. This would require 57 // changing the loader's TypeCheckFuncBodies hook to 58 // provide the []*ast.File. 59 } 60 61 // Load/parse/type-check the program. 62 lprog, err := lconf.Load() 63 if err != nil { 64 return err 65 } 66 67 qpos, err := parseQueryPos(lprog, q.Pos, false) 68 if err != nil { 69 return err 70 } 71 72 // Find the selected type. 73 path, action := findInterestingNode(qpos.info, qpos.path) 74 75 var method *types.Func 76 var T types.Type // selected type (receiver if method != nil) 77 78 switch action { 79 case actionExpr: 80 // method? 81 if id, ok := path[0].(*ast.Ident); ok { 82 if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok { 83 recv := obj.Type().(*types.Signature).Recv() 84 if recv == nil { 85 return fmt.Errorf("this function is not a method") 86 } 87 method = obj 88 T = recv.Type() 89 } 90 } 91 92 // If not a method, use the expression's type. 93 if T == nil { 94 T = qpos.info.TypeOf(path[0].(ast.Expr)) 95 } 96 97 case actionType: 98 T = qpos.info.TypeOf(path[0].(ast.Expr)) 99 } 100 if T == nil { 101 return fmt.Errorf("not a type, method, or value") 102 } 103 104 // Find all named types, even local types (which can have 105 // methods due to promotion) and the built-in "error". 106 // We ignore aliases 'type M = N' to avoid duplicate 107 // reporting of the Named type N. 108 var allNamed []*types.Named 109 for _, info := range lprog.AllPackages { 110 for _, obj := range info.Defs { 111 if obj, ok := obj.(*types.TypeName); ok && !isAlias(obj) { 112 if named, ok := obj.Type().(*types.Named); ok { 113 allNamed = append(allNamed, named) 114 } 115 } 116 } 117 } 118 allNamed = append(allNamed, types.Universe.Lookup("error").Type().(*types.Named)) 119 120 var msets typeutil.MethodSetCache 121 122 // Test each named type. 123 var to, from, fromPtr []types.Type 124 for _, U := range allNamed { 125 if isInterface(T) { 126 if msets.MethodSet(T).Len() == 0 { 127 continue // empty interface 128 } 129 if isInterface(U) { 130 if msets.MethodSet(U).Len() == 0 { 131 continue // empty interface 132 } 133 134 // T interface, U interface 135 if !types.Identical(T, U) { 136 if types.AssignableTo(U, T) { 137 to = append(to, U) 138 } 139 if types.AssignableTo(T, U) { 140 from = append(from, U) 141 } 142 } 143 } else { 144 // T interface, U concrete 145 if types.AssignableTo(U, T) { 146 to = append(to, U) 147 } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) { 148 to = append(to, pU) 149 } 150 } 151 } else if isInterface(U) { 152 if msets.MethodSet(U).Len() == 0 { 153 continue // empty interface 154 } 155 156 // T concrete, U interface 157 if types.AssignableTo(T, U) { 158 from = append(from, U) 159 } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) { 160 fromPtr = append(fromPtr, U) 161 } 162 } 163 } 164 165 var pos interface{} = qpos 166 if nt, ok := deref(T).(*types.Named); ok { 167 pos = nt.Obj() 168 } 169 170 // Sort types (arbitrarily) to ensure test determinism. 171 sort.Sort(typesByString(to)) 172 sort.Sort(typesByString(from)) 173 sort.Sort(typesByString(fromPtr)) 174 175 var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils 176 if method != nil { 177 for _, t := range to { 178 toMethod = append(toMethod, 179 types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) 180 } 181 for _, t := range from { 182 fromMethod = append(fromMethod, 183 types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) 184 } 185 for _, t := range fromPtr { 186 fromPtrMethod = append(fromPtrMethod, 187 types.NewMethodSet(t).Lookup(method.Pkg(), method.Name())) 188 } 189 } 190 191 q.Output(lprog.Fset, &implementsResult{ 192 qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod, 193 }) 194 return nil 195 } 196 197 type implementsResult struct { 198 qpos *queryPos 199 200 t types.Type // queried type (not necessarily named) 201 pos interface{} // pos of t (*types.Name or *QueryPos) 202 to []types.Type // named or ptr-to-named types assignable to interface T 203 from []types.Type // named interfaces assignable from T 204 fromPtr []types.Type // named interfaces assignable only from *T 205 206 // if a method was queried: 207 method *types.Func // queried method 208 toMethod []*types.Selection // method of type to[i], if any 209 fromMethod []*types.Selection // method of type from[i], if any 210 fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any 211 } 212 213 func (r *implementsResult) PrintPlain(printf printfFunc) { 214 relation := "is implemented by" 215 216 meth := func(sel *types.Selection) { 217 if sel != nil { 218 printf(sel.Obj(), "\t%s method (%s).%s", 219 relation, r.qpos.typeString(sel.Recv()), sel.Obj().Name()) 220 } 221 } 222 223 if isInterface(r.t) { 224 if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset 225 printf(r.pos, "empty interface type %s", r.qpos.typeString(r.t)) 226 return 227 } 228 229 if r.method == nil { 230 printf(r.pos, "interface type %s", r.qpos.typeString(r.t)) 231 } else { 232 printf(r.method, "abstract method %s", r.qpos.objectString(r.method)) 233 } 234 235 // Show concrete types (or methods) first; use two passes. 236 for i, sub := range r.to { 237 if !isInterface(sub) { 238 if r.method == nil { 239 printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s", 240 relation, typeKind(sub), r.qpos.typeString(sub)) 241 } else { 242 meth(r.toMethod[i]) 243 } 244 } 245 } 246 for i, sub := range r.to { 247 if isInterface(sub) { 248 if r.method == nil { 249 printf(sub.(*types.Named).Obj(), "\t%s %s type %s", 250 relation, typeKind(sub), r.qpos.typeString(sub)) 251 } else { 252 meth(r.toMethod[i]) 253 } 254 } 255 } 256 257 relation = "implements" 258 for i, super := range r.from { 259 if r.method == nil { 260 printf(super.(*types.Named).Obj(), "\t%s %s", 261 relation, r.qpos.typeString(super)) 262 } else { 263 meth(r.fromMethod[i]) 264 } 265 } 266 } else { 267 relation = "implements" 268 269 if r.from != nil { 270 if r.method == nil { 271 printf(r.pos, "%s type %s", 272 typeKind(r.t), r.qpos.typeString(r.t)) 273 } else { 274 printf(r.method, "concrete method %s", 275 r.qpos.objectString(r.method)) 276 } 277 for i, super := range r.from { 278 if r.method == nil { 279 printf(super.(*types.Named).Obj(), "\t%s %s", 280 relation, r.qpos.typeString(super)) 281 } else { 282 meth(r.fromMethod[i]) 283 } 284 } 285 } 286 if r.fromPtr != nil { 287 if r.method == nil { 288 printf(r.pos, "pointer type *%s", r.qpos.typeString(r.t)) 289 } else { 290 // TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f. 291 printf(r.method, "concrete method %s", 292 r.qpos.objectString(r.method)) 293 } 294 295 for i, psuper := range r.fromPtr { 296 if r.method == nil { 297 printf(psuper.(*types.Named).Obj(), "\t%s %s", 298 relation, r.qpos.typeString(psuper)) 299 } else { 300 meth(r.fromPtrMethod[i]) 301 } 302 } 303 } else if r.from == nil { 304 printf(r.pos, "%s type %s implements only interface{}", 305 typeKind(r.t), r.qpos.typeString(r.t)) 306 } 307 } 308 } 309 310 func (r *implementsResult) JSON(fset *token.FileSet) []byte { 311 var method *serial.DescribeMethod 312 if r.method != nil { 313 method = &serial.DescribeMethod{ 314 Name: r.qpos.objectString(r.method), 315 Pos: fset.Position(r.method.Pos()).String(), 316 } 317 } 318 return toJSON(&serial.Implements{ 319 T: makeImplementsType(r.t, fset), 320 AssignableTo: makeImplementsTypes(r.to, fset), 321 AssignableFrom: makeImplementsTypes(r.from, fset), 322 AssignableFromPtr: makeImplementsTypes(r.fromPtr, fset), 323 AssignableToMethod: methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset), 324 AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset), 325 AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset), 326 Method: method, 327 }) 328 329 } 330 331 func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType { 332 var r []serial.ImplementsType 333 for _, t := range tt { 334 r = append(r, makeImplementsType(t, fset)) 335 } 336 return r 337 } 338 339 func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType { 340 var pos token.Pos 341 if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named 342 pos = nt.Obj().Pos() 343 } 344 return serial.ImplementsType{ 345 Name: T.String(), 346 Pos: fset.Position(pos).String(), 347 Kind: typeKind(T), 348 } 349 } 350 351 // typeKind returns a string describing the underlying kind of type, 352 // e.g. "slice", "array", "struct". 353 func typeKind(T types.Type) string { 354 s := reflect.TypeOf(T.Underlying()).String() 355 return strings.ToLower(strings.TrimPrefix(s, "*types.")) 356 } 357 358 func isInterface(T types.Type) bool { return types.IsInterface(T) } 359 360 type typesByString []types.Type 361 362 func (p typesByString) Len() int { return len(p) } 363 func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() } 364 func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }