Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / cmd / guru / implements.go
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         "golang.org/x/tools/cmd/guru/serial"
17         "golang.org/x/tools/go/loader"
18         "golang.org/x/tools/go/types/typeutil"
19         "golang.org/x/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] }