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 / freevars.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         "bytes"
9         "go/ast"
10         "go/printer"
11         "go/token"
12         "go/types"
13         "sort"
14
15         "golang.org/x/tools/cmd/guru/serial"
16         "golang.org/x/tools/go/loader"
17 )
18
19 // freevars displays the lexical (not package-level) free variables of
20 // the selection.
21 //
22 // It treats A.B.C as a separate variable from A to reveal the parts
23 // of an aggregate type that are actually needed.
24 // This aids refactoring.
25 //
26 // TODO(adonovan): optionally display the free references to
27 // file/package scope objects, and to objects from other packages.
28 // Depending on where the resulting function abstraction will go,
29 // these might be interesting.  Perhaps group the results into three
30 // bands.
31 //
32 func freevars(q *Query) error {
33         lconf := loader.Config{Build: q.Build}
34         allowErrors(&lconf)
35
36         if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
37                 return err
38         }
39
40         // Load/parse/type-check the program.
41         lprog, err := lconf.Load()
42         if err != nil {
43                 return err
44         }
45
46         qpos, err := parseQueryPos(lprog, q.Pos, false)
47         if err != nil {
48                 return err
49         }
50
51         file := qpos.path[len(qpos.path)-1] // the enclosing file
52         fileScope := qpos.info.Scopes[file]
53         pkgScope := fileScope.Parent()
54
55         // The id and sel functions return non-nil if they denote an
56         // object o or selection o.x.y that is referenced by the
57         // selection but defined neither within the selection nor at
58         // file scope, i.e. it is in the lexical environment.
59         var id func(n *ast.Ident) types.Object
60         var sel func(n *ast.SelectorExpr) types.Object
61
62         sel = func(n *ast.SelectorExpr) types.Object {
63                 switch x := unparen(n.X).(type) {
64                 case *ast.SelectorExpr:
65                         return sel(x)
66                 case *ast.Ident:
67                         return id(x)
68                 }
69                 return nil
70         }
71
72         id = func(n *ast.Ident) types.Object {
73                 obj := qpos.info.Uses[n]
74                 if obj == nil {
75                         return nil // not a reference
76                 }
77                 if _, ok := obj.(*types.PkgName); ok {
78                         return nil // imported package
79                 }
80                 if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) {
81                         return nil // not defined in this file
82                 }
83                 scope := obj.Parent()
84                 if scope == nil {
85                         return nil // e.g. interface method, struct field
86                 }
87                 if scope == fileScope || scope == pkgScope {
88                         return nil // defined at file or package scope
89                 }
90                 if qpos.start <= obj.Pos() && obj.Pos() <= qpos.end {
91                         return nil // defined within selection => not free
92                 }
93                 return obj
94         }
95
96         // Maps each reference that is free in the selection
97         // to the object it refers to.
98         // The map de-duplicates repeated references.
99         refsMap := make(map[string]freevarsRef)
100
101         // Visit all the identifiers in the selected ASTs.
102         ast.Inspect(qpos.path[0], func(n ast.Node) bool {
103                 if n == nil {
104                         return true // popping DFS stack
105                 }
106
107                 // Is this node contained within the selection?
108                 // (freevars permits inexact selections,
109                 // like two stmts in a block.)
110                 if qpos.start <= n.Pos() && n.End() <= qpos.end {
111                         var obj types.Object
112                         var prune bool
113                         switch n := n.(type) {
114                         case *ast.Ident:
115                                 obj = id(n)
116
117                         case *ast.SelectorExpr:
118                                 obj = sel(n)
119                                 prune = true
120                         }
121
122                         if obj != nil {
123                                 var kind string
124                                 switch obj.(type) {
125                                 case *types.Var:
126                                         kind = "var"
127                                 case *types.Func:
128                                         kind = "func"
129                                 case *types.TypeName:
130                                         kind = "type"
131                                 case *types.Const:
132                                         kind = "const"
133                                 case *types.Label:
134                                         kind = "label"
135                                 default:
136                                         panic(obj)
137                                 }
138
139                                 typ := qpos.info.TypeOf(n.(ast.Expr))
140                                 ref := freevarsRef{kind, printNode(lprog.Fset, n), typ, obj}
141                                 refsMap[ref.ref] = ref
142
143                                 if prune {
144                                         return false // don't descend
145                                 }
146                         }
147                 }
148
149                 return true // descend
150         })
151
152         refs := make([]freevarsRef, 0, len(refsMap))
153         for _, ref := range refsMap {
154                 refs = append(refs, ref)
155         }
156         sort.Sort(byRef(refs))
157
158         q.Output(lprog.Fset, &freevarsResult{
159                 qpos: qpos,
160                 refs: refs,
161         })
162         return nil
163 }
164
165 type freevarsResult struct {
166         qpos *queryPos
167         refs []freevarsRef
168 }
169
170 type freevarsRef struct {
171         kind string
172         ref  string
173         typ  types.Type
174         obj  types.Object
175 }
176
177 func (r *freevarsResult) PrintPlain(printf printfFunc) {
178         if len(r.refs) == 0 {
179                 printf(r.qpos, "No free identifiers.")
180         } else {
181                 printf(r.qpos, "Free identifiers:")
182                 qualifier := types.RelativeTo(r.qpos.info.Pkg)
183                 for _, ref := range r.refs {
184                         // Avoid printing "type T T".
185                         var typstr string
186                         if ref.kind != "type" && ref.kind != "label" {
187                                 typstr = " " + types.TypeString(ref.typ, qualifier)
188                         }
189                         printf(ref.obj, "%s %s%s", ref.kind, ref.ref, typstr)
190                 }
191         }
192 }
193
194 func (r *freevarsResult) JSON(fset *token.FileSet) []byte {
195         var buf bytes.Buffer
196         for i, ref := range r.refs {
197                 if i > 0 {
198                         buf.WriteByte('\n')
199                 }
200                 buf.Write(toJSON(serial.FreeVar{
201                         Pos:  fset.Position(ref.obj.Pos()).String(),
202                         Kind: ref.kind,
203                         Ref:  ref.ref,
204                         Type: ref.typ.String(),
205                 }))
206         }
207         return buf.Bytes()
208 }
209
210 // -------- utils --------
211
212 type byRef []freevarsRef
213
214 func (p byRef) Len() int           { return len(p) }
215 func (p byRef) Less(i, j int) bool { return p[i].ref < p[j].ref }
216 func (p byRef) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
217
218 // printNode returns the pretty-printed syntax of n.
219 func printNode(fset *token.FileSet, n ast.Node) string {
220         var buf bytes.Buffer
221         printer.Fprint(&buf, fset, n)
222         return buf.String()
223 }