Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / cmd / guru / guru.go
1 // Copyright 2014 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 // TODO(adonovan): new queries
8 // - show all statements that may update the selected lvalue
9 //   (local, global, field, etc).
10 // - show all places where an object of type T is created
11 //   (&T{}, var t T, new(T), new(struct{array [3]T}), etc.
12
13 import (
14         "encoding/json"
15         "fmt"
16         "go/ast"
17         "go/build"
18         "go/parser"
19         "go/token"
20         "go/types"
21         "io"
22         "log"
23         "path/filepath"
24         "strings"
25
26         "golang.org/x/tools/go/ast/astutil"
27         "golang.org/x/tools/go/buildutil"
28         "golang.org/x/tools/go/loader"
29         "golang.org/x/tools/go/pointer"
30         "golang.org/x/tools/go/ssa"
31 )
32
33 type printfFunc func(pos interface{}, format string, args ...interface{})
34
35 // A QueryResult is an item of output.  Each query produces a stream of
36 // query results, calling Query.Output for each one.
37 type QueryResult interface {
38         // JSON returns the QueryResult in JSON form.
39         JSON(fset *token.FileSet) []byte
40
41         // PrintPlain prints the QueryResult in plain text form.
42         // The implementation calls printfFunc to print each line of output.
43         PrintPlain(printf printfFunc)
44 }
45
46 // A QueryPos represents the position provided as input to a query:
47 // a textual extent in the program's source code, the AST node it
48 // corresponds to, and the package to which it belongs.
49 // Instances are created by parseQueryPos.
50 type queryPos struct {
51         fset       *token.FileSet
52         start, end token.Pos           // source extent of query
53         path       []ast.Node          // AST path from query node to root of ast.File
54         exact      bool                // 2nd result of PathEnclosingInterval
55         info       *loader.PackageInfo // type info for the queried package (nil for fastQueryPos)
56 }
57
58 // TypeString prints type T relative to the query position.
59 func (qpos *queryPos) typeString(T types.Type) string {
60         return types.TypeString(T, types.RelativeTo(qpos.info.Pkg))
61 }
62
63 // ObjectString prints object obj relative to the query position.
64 func (qpos *queryPos) objectString(obj types.Object) string {
65         return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg))
66 }
67
68 // A Query specifies a single guru query.
69 type Query struct {
70         Pos   string         // query position
71         Build *build.Context // package loading configuration
72
73         // pointer analysis options
74         Scope      []string  // main packages in (*loader.Config).FromArgs syntax
75         PTALog     io.Writer // (optional) pointer-analysis log file
76         Reflection bool      // model reflection soundly (currently slow).
77
78         // result-printing function, safe for concurrent use
79         Output func(*token.FileSet, QueryResult)
80 }
81
82 // Run runs an guru query and populates its Fset and Result.
83 func Run(mode string, q *Query) error {
84         switch mode {
85         case "callees":
86                 return callees(q)
87         case "callers":
88                 return callers(q)
89         case "callstack":
90                 return callstack(q)
91         case "peers":
92                 return peers(q)
93         case "pointsto":
94                 return pointsto(q)
95         case "whicherrs":
96                 return whicherrs(q)
97         case "definition":
98                 return definition(q)
99         case "describe":
100                 return describe(q)
101         case "freevars":
102                 return freevars(q)
103         case "implements":
104                 return implements(q)
105         case "referrers":
106                 return referrers(q)
107         case "what":
108                 return what(q)
109         default:
110                 return fmt.Errorf("invalid mode: %q", mode)
111         }
112 }
113
114 func setPTAScope(lconf *loader.Config, scope []string) error {
115         pkgs := buildutil.ExpandPatterns(lconf.Build, scope)
116         if len(pkgs) == 0 {
117                 return fmt.Errorf("no packages specified for pointer analysis scope")
118         }
119         // The value of each entry in pkgs is true,
120         // giving ImportWithTests (not Import) semantics.
121         lconf.ImportPkgs = pkgs
122         return nil
123 }
124
125 // Create a pointer.Config whose scope is the initial packages of lprog
126 // and their dependencies.
127 func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) {
128         // For each initial package (specified on the command line),
129         // if it has a main function, analyze that,
130         // otherwise analyze its tests, if any.
131         var mains []*ssa.Package
132         for _, info := range lprog.InitialPackages() {
133                 p := prog.Package(info.Pkg)
134
135                 // Add package to the pointer analysis scope.
136                 if p.Pkg.Name() == "main" && p.Func("main") != nil {
137                         mains = append(mains, p)
138                 } else if main := prog.CreateTestMainPackage(p); main != nil {
139                         mains = append(mains, main)
140                 }
141         }
142         if mains == nil {
143                 return nil, fmt.Errorf("analysis scope has no main and no tests")
144         }
145         return &pointer.Config{
146                 Log:        ptaLog,
147                 Reflection: reflection,
148                 Mains:      mains,
149         }, nil
150 }
151
152 // importQueryPackage finds the package P containing the
153 // query position and tells conf to import it.
154 // It returns the package's path.
155 func importQueryPackage(pos string, conf *loader.Config) (string, error) {
156         fqpos, err := fastQueryPos(conf.Build, pos)
157         if err != nil {
158                 return "", err // bad query
159         }
160         filename := fqpos.fset.File(fqpos.start).Name()
161
162         _, importPath, err := guessImportPath(filename, conf.Build)
163         if err != nil {
164                 // Can't find GOPATH dir.
165                 // Treat the query file as its own package.
166                 importPath = "command-line-arguments"
167                 conf.CreateFromFilenames(importPath, filename)
168         } else {
169                 // Check that it's possible to load the queried package.
170                 // (e.g. guru tests contain different 'package' decls in same dir.)
171                 // Keep consistent with logic in loader/util.go!
172                 cfg2 := *conf.Build
173                 cfg2.CgoEnabled = false
174                 bp, err := cfg2.Import(importPath, "", 0)
175                 if err != nil {
176                         return "", err // no files for package
177                 }
178
179                 switch pkgContainsFile(bp, filename) {
180                 case 'T':
181                         conf.ImportWithTests(importPath)
182                 case 'X':
183                         conf.ImportWithTests(importPath)
184                         importPath += "_test" // for TypeCheckFuncBodies
185                 case 'G':
186                         conf.Import(importPath)
187                 default:
188                         // This happens for ad-hoc packages like
189                         // $GOROOT/src/net/http/triv.go.
190                         return "", fmt.Errorf("package %q doesn't contain file %s",
191                                 importPath, filename)
192                 }
193         }
194
195         conf.TypeCheckFuncBodies = func(p string) bool { return p == importPath }
196
197         return importPath, nil
198 }
199
200 // pkgContainsFile reports whether file was among the packages Go
201 // files, Test files, eXternal test files, or not found.
202 func pkgContainsFile(bp *build.Package, filename string) byte {
203         for i, files := range [][]string{bp.GoFiles, bp.TestGoFiles, bp.XTestGoFiles} {
204                 for _, file := range files {
205                         if sameFile(filepath.Join(bp.Dir, file), filename) {
206                                 return "GTX"[i]
207                         }
208                 }
209         }
210         return 0 // not found
211 }
212
213 // ParseQueryPos parses the source query position pos and returns the
214 // AST node of the loaded program lprog that it identifies.
215 // If needExact, it must identify a single AST subtree;
216 // this is appropriate for queries that allow fairly arbitrary syntax,
217 // e.g. "describe".
218 //
219 func parseQueryPos(lprog *loader.Program, pos string, needExact bool) (*queryPos, error) {
220         filename, startOffset, endOffset, err := parsePos(pos)
221         if err != nil {
222                 return nil, err
223         }
224
225         // Find the named file among those in the loaded program.
226         var file *token.File
227         lprog.Fset.Iterate(func(f *token.File) bool {
228                 if sameFile(filename, f.Name()) {
229                         file = f
230                         return false // done
231                 }
232                 return true // continue
233         })
234         if file == nil {
235                 return nil, fmt.Errorf("file %s not found in loaded program", filename)
236         }
237
238         start, end, err := fileOffsetToPos(file, startOffset, endOffset)
239         if err != nil {
240                 return nil, err
241         }
242         info, path, exact := lprog.PathEnclosingInterval(start, end)
243         if path == nil {
244                 return nil, fmt.Errorf("no syntax here")
245         }
246         if needExact && !exact {
247                 return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0]))
248         }
249         return &queryPos{lprog.Fset, start, end, path, exact, info}, nil
250 }
251
252 // ---------- Utilities ----------
253
254 // loadWithSoftErrors calls lconf.Load, suppressing "soft" errors.  (See Go issue 16530.)
255 // TODO(adonovan): Once the loader has an option to allow soft errors,
256 // replace calls to loadWithSoftErrors with loader calls with that parameter.
257 func loadWithSoftErrors(lconf *loader.Config) (*loader.Program, error) {
258         lconf.AllowErrors = true
259
260         // Ideally we would just return conf.Load() here, but go/types
261         // reports certain "soft" errors that gc does not (Go issue 14596).
262         // As a workaround, we set AllowErrors=true and then duplicate
263         // the loader's error checking but allow soft errors.
264         // It would be nice if the loader API permitted "AllowErrors: soft".
265         prog, err := lconf.Load()
266         if err != nil {
267                 return nil, err
268         }
269         var errpkgs []string
270         // Report hard errors in indirectly imported packages.
271         for _, info := range prog.AllPackages {
272                 if containsHardErrors(info.Errors) {
273                         errpkgs = append(errpkgs, info.Pkg.Path())
274                 } else {
275                         // Enable SSA construction for packages containing only soft errors.
276                         info.TransitivelyErrorFree = true
277                 }
278         }
279         if errpkgs != nil {
280                 var more string
281                 if len(errpkgs) > 3 {
282                         more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
283                         errpkgs = errpkgs[:3]
284                 }
285                 return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
286                         strings.Join(errpkgs, ", "), more)
287         }
288         return prog, err
289 }
290
291 func containsHardErrors(errors []error) bool {
292         for _, err := range errors {
293                 if err, ok := err.(types.Error); ok && err.Soft {
294                         continue
295                 }
296                 return true
297         }
298         return false
299 }
300
301 // allowErrors causes type errors to be silently ignored.
302 // (Not suitable if SSA construction follows.)
303 func allowErrors(lconf *loader.Config) {
304         ctxt := *lconf.Build // copy
305         ctxt.CgoEnabled = false
306         lconf.Build = &ctxt
307         lconf.AllowErrors = true
308         // AllErrors makes the parser always return an AST instead of
309         // bailing out after 10 errors and returning an empty ast.File.
310         lconf.ParserMode = parser.AllErrors
311         lconf.TypeChecker.Error = func(err error) {}
312 }
313
314 // ptrAnalysis runs the pointer analysis and returns its result.
315 func ptrAnalysis(conf *pointer.Config) *pointer.Result {
316         result, err := pointer.Analyze(conf)
317         if err != nil {
318                 panic(err) // pointer analysis internal error
319         }
320         return result
321 }
322
323 func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
324
325 // deref returns a pointer's element type; otherwise it returns typ.
326 func deref(typ types.Type) types.Type {
327         if p, ok := typ.Underlying().(*types.Pointer); ok {
328                 return p.Elem()
329         }
330         return typ
331 }
332
333 // fprintf prints to w a message of the form "location: message\n"
334 // where location is derived from pos.
335 //
336 // pos must be one of:
337 //    - a token.Pos, denoting a position
338 //    - an ast.Node, denoting an interval
339 //    - anything with a Pos() method:
340 //         ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
341 //    - a QueryPos, denoting the extent of the user's query.
342 //    - nil, meaning no position at all.
343 //
344 // The output format is is compatible with the 'gnu'
345 // compilation-error-regexp in Emacs' compilation mode.
346 //
347 func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) {
348         var start, end token.Pos
349         switch pos := pos.(type) {
350         case ast.Node:
351                 start = pos.Pos()
352                 end = pos.End()
353         case token.Pos:
354                 start = pos
355                 end = start
356         case *types.PkgName:
357                 // The Pos of most PkgName objects does not coincide with an identifier,
358                 // so we suppress the usual start+len(name) heuristic for types.Objects.
359                 start = pos.Pos()
360                 end = start
361         case types.Object:
362                 start = pos.Pos()
363                 end = start + token.Pos(len(pos.Name())) // heuristic
364         case interface {
365                 Pos() token.Pos
366         }:
367                 start = pos.Pos()
368                 end = start
369         case *queryPos:
370                 start = pos.start
371                 end = pos.end
372         case nil:
373                 // no-op
374         default:
375                 panic(fmt.Sprintf("invalid pos: %T", pos))
376         }
377
378         if sp := fset.Position(start); start == end {
379                 // (prints "-: " for token.NoPos)
380                 fmt.Fprintf(w, "%s: ", sp)
381         } else {
382                 ep := fset.Position(end)
383                 // The -1 below is a concession to Emacs's broken use of
384                 // inclusive (not half-open) intervals.
385                 // Other editors may not want it.
386                 // TODO(adonovan): add an -editor=vim|emacs|acme|auto
387                 // flag; auto uses EMACS=t / VIM=... / etc env vars.
388                 fmt.Fprintf(w, "%s:%d.%d-%d.%d: ",
389                         sp.Filename, sp.Line, sp.Column, ep.Line, ep.Column-1)
390         }
391         fmt.Fprintf(w, format, args...)
392         io.WriteString(w, "\n")
393 }
394
395 func toJSON(x interface{}) []byte {
396         b, err := json.MarshalIndent(x, "", "\t")
397         if err != nil {
398                 log.Fatalf("JSON error: %v", err)
399         }
400         return b
401 }