blob: 07a617dc4942f1a03d4c15db9fd6a3ac2b91b27e [file] [log] [blame]
Alan Donovan73473792016-02-11 17:57:17 -05001// 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
5package main
6
7import (
8 "fmt"
9 "go/ast"
10 "go/token"
11 "go/types"
12 "reflect"
13 "sort"
14 "strings"
15
Alan Donovan37bb37e2016-02-11 22:57:18 -050016 "golang.org/x/tools/cmd/guru/serial"
Alan Donovan73473792016-02-11 17:57:17 -050017 "golang.org/x/tools/go/loader"
18 "golang.org/x/tools/go/types/typeutil"
Alan Donovan73473792016-02-11 17:57:17 -050019 "golang.org/x/tools/refactor/importgraph"
20)
21
22// Implements 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//
28func 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 q.Fset = lprog.Fset
67
68 qpos, err := parseQueryPos(lprog, q.Pos, false)
69 if err != nil {
70 return err
71 }
72
73 // Find the selected type.
74 path, action := findInterestingNode(qpos.info, qpos.path)
75
76 var method *types.Func
77 var T types.Type // selected type (receiver if method != nil)
78
79 switch action {
80 case actionExpr:
81 // method?
82 if id, ok := path[0].(*ast.Ident); ok {
83 if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
84 recv := obj.Type().(*types.Signature).Recv()
85 if recv == nil {
86 return fmt.Errorf("this function is not a method")
87 }
88 method = obj
89 T = recv.Type()
90 }
91 }
92 case actionType:
93 T = qpos.info.TypeOf(path[0].(ast.Expr))
94 }
95 if T == nil {
96 return fmt.Errorf("no type or method here")
97 }
98
99 // Find all named types, even local types (which can have
100 // methods via promotion) and the built-in "error".
101 var allNamed []types.Type
102 for _, info := range lprog.AllPackages {
103 for _, obj := range info.Defs {
104 if obj, ok := obj.(*types.TypeName); ok {
105 allNamed = append(allNamed, obj.Type())
106 }
107 }
108 }
109 allNamed = append(allNamed, types.Universe.Lookup("error").Type())
110
111 var msets typeutil.MethodSetCache
112
113 // Test each named type.
114 var to, from, fromPtr []types.Type
115 for _, U := range allNamed {
116 if isInterface(T) {
117 if msets.MethodSet(T).Len() == 0 {
118 continue // empty interface
119 }
120 if isInterface(U) {
121 if msets.MethodSet(U).Len() == 0 {
122 continue // empty interface
123 }
124
125 // T interface, U interface
126 if !types.Identical(T, U) {
127 if types.AssignableTo(U, T) {
128 to = append(to, U)
129 }
130 if types.AssignableTo(T, U) {
131 from = append(from, U)
132 }
133 }
134 } else {
135 // T interface, U concrete
136 if types.AssignableTo(U, T) {
137 to = append(to, U)
138 } else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
139 to = append(to, pU)
140 }
141 }
142 } else if isInterface(U) {
143 if msets.MethodSet(U).Len() == 0 {
144 continue // empty interface
145 }
146
147 // T concrete, U interface
148 if types.AssignableTo(T, U) {
149 from = append(from, U)
150 } else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
151 fromPtr = append(fromPtr, U)
152 }
153 }
154 }
155
156 var pos interface{} = qpos
157 if nt, ok := deref(T).(*types.Named); ok {
158 pos = nt.Obj()
159 }
160
161 // Sort types (arbitrarily) to ensure test determinism.
162 sort.Sort(typesByString(to))
163 sort.Sort(typesByString(from))
164 sort.Sort(typesByString(fromPtr))
165
166 var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
167 if method != nil {
168 for _, t := range to {
169 toMethod = append(toMethod,
170 types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
171 }
172 for _, t := range from {
173 fromMethod = append(fromMethod,
174 types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
175 }
176 for _, t := range fromPtr {
177 fromPtrMethod = append(fromPtrMethod,
178 types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
179 }
180 }
181
182 q.result = &implementsResult{
183 qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod,
184 }
185 return nil
186}
187
188type implementsResult struct {
189 qpos *queryPos
190
191 t types.Type // queried type (not necessarily named)
192 pos interface{} // pos of t (*types.Name or *QueryPos)
193 to []types.Type // named or ptr-to-named types assignable to interface T
194 from []types.Type // named interfaces assignable from T
195 fromPtr []types.Type // named interfaces assignable only from *T
196
197 // if a method was queried:
198 method *types.Func // queried method
199 toMethod []*types.Selection // method of type to[i], if any
200 fromMethod []*types.Selection // method of type from[i], if any
201 fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any
202}
203
204func (r *implementsResult) display(printf printfFunc) {
205 relation := "is implemented by"
206
207 meth := func(sel *types.Selection) {
208 if sel != nil {
209 printf(sel.Obj(), "\t%s method (%s).%s",
210 relation, r.qpos.typeString(sel.Recv()), sel.Obj().Name())
211 }
212 }
213
214 if isInterface(r.t) {
215 if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset
216 printf(r.pos, "empty interface type %s", r.qpos.typeString(r.t))
217 return
218 }
219
220 if r.method == nil {
221 printf(r.pos, "interface type %s", r.qpos.typeString(r.t))
222 } else {
223 printf(r.method, "abstract method %s", r.qpos.objectString(r.method))
224 }
225
226 // Show concrete types (or methods) first; use two passes.
227 for i, sub := range r.to {
228 if !isInterface(sub) {
229 if r.method == nil {
230 printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s",
231 relation, typeKind(sub), r.qpos.typeString(sub))
232 } else {
233 meth(r.toMethod[i])
234 }
235 }
236 }
237 for i, sub := range r.to {
238 if isInterface(sub) {
239 if r.method == nil {
240 printf(sub.(*types.Named).Obj(), "\t%s %s type %s",
241 relation, typeKind(sub), r.qpos.typeString(sub))
242 } else {
243 meth(r.toMethod[i])
244 }
245 }
246 }
247
248 relation = "implements"
249 for i, super := range r.from {
250 if r.method == nil {
251 printf(super.(*types.Named).Obj(), "\t%s %s",
252 relation, r.qpos.typeString(super))
253 } else {
254 meth(r.fromMethod[i])
255 }
256 }
257 } else {
258 relation = "implements"
259
260 if r.from != nil {
261 if r.method == nil {
262 printf(r.pos, "%s type %s",
263 typeKind(r.t), r.qpos.typeString(r.t))
264 } else {
265 printf(r.method, "concrete method %s",
266 r.qpos.objectString(r.method))
267 }
268 for i, super := range r.from {
269 if r.method == nil {
270 printf(super.(*types.Named).Obj(), "\t%s %s",
271 relation, r.qpos.typeString(super))
272 } else {
273 meth(r.fromMethod[i])
274 }
275 }
276 }
277 if r.fromPtr != nil {
278 if r.method == nil {
279 printf(r.pos, "pointer type *%s", r.qpos.typeString(r.t))
280 } else {
281 // TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f.
282 printf(r.method, "concrete method %s",
283 r.qpos.objectString(r.method))
284 }
285
286 for i, psuper := range r.fromPtr {
287 if r.method == nil {
288 printf(psuper.(*types.Named).Obj(), "\t%s %s",
289 relation, r.qpos.typeString(psuper))
290 } else {
291 meth(r.fromPtrMethod[i])
292 }
293 }
294 } else if r.from == nil {
295 printf(r.pos, "%s type %s implements only interface{}",
296 typeKind(r.t), r.qpos.typeString(r.t))
297 }
298 }
299}
300
301func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
302 res.Implements = &serial.Implements{
303 T: makeImplementsType(r.t, fset),
304 AssignableTo: makeImplementsTypes(r.to, fset),
305 AssignableFrom: makeImplementsTypes(r.from, fset),
306 AssignableFromPtr: makeImplementsTypes(r.fromPtr, fset),
307 AssignableToMethod: methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset),
308 AssignableFromMethod: methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset),
309 AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset),
310 }
311 if r.method != nil {
312 res.Implements.Method = &serial.DescribeMethod{
313 Name: r.qpos.objectString(r.method),
314 Pos: fset.Position(r.method.Pos()).String(),
315 }
316 }
317}
318
319func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType {
320 var r []serial.ImplementsType
321 for _, t := range tt {
322 r = append(r, makeImplementsType(t, fset))
323 }
324 return r
325}
326
327func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType {
328 var pos token.Pos
329 if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named
330 pos = nt.Obj().Pos()
331 }
332 return serial.ImplementsType{
333 Name: T.String(),
334 Pos: fset.Position(pos).String(),
335 Kind: typeKind(T),
336 }
337}
338
339// typeKind returns a string describing the underlying kind of type,
340// e.g. "slice", "array", "struct".
341func typeKind(T types.Type) string {
342 s := reflect.TypeOf(T.Underlying()).String()
343 return strings.ToLower(strings.TrimPrefix(s, "*types."))
344}
345
346func isInterface(T types.Type) bool { return types.IsInterface(T) }
347
348type typesByString []types.Type
349
350func (p typesByString) Len() int { return len(p) }
351func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
352func (p typesByString) Swap(i, j int) { p[i], p[j] = p[j], p[i] }