You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
9.9 KiB

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