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 / whicherrs.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 import (
8         "fmt"
9         "go/ast"
10         "go/token"
11         "go/types"
12         "sort"
13
14         "golang.org/x/tools/cmd/guru/serial"
15         "golang.org/x/tools/go/ast/astutil"
16         "golang.org/x/tools/go/loader"
17         "golang.org/x/tools/go/pointer"
18         "golang.org/x/tools/go/ssa"
19         "golang.org/x/tools/go/ssa/ssautil"
20 )
21
22 var builtinErrorType = types.Universe.Lookup("error").Type()
23
24 // whicherrs takes an position to an error and tries to find all types, constants
25 // and global value which a given error can point to and which can be checked from the
26 // scope where the error lives.
27 // In short, it returns a list of things that can be checked against in order to handle
28 // an error properly.
29 //
30 // TODO(dmorsing): figure out if fields in errors like *os.PathError.Err
31 // can be queried recursively somehow.
32 func whicherrs(q *Query) error {
33         lconf := loader.Config{Build: q.Build}
34
35         if err := setPTAScope(&lconf, q.Scope); err != nil {
36                 return err
37         }
38
39         // Load/parse/type-check the program.
40         lprog, err := loadWithSoftErrors(&lconf)
41         if err != nil {
42                 return err
43         }
44
45         qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
46         if err != nil {
47                 return err
48         }
49
50         prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
51
52         ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
53         if err != nil {
54                 return err
55         }
56
57         path, action := findInterestingNode(qpos.info, qpos.path)
58         if action != actionExpr {
59                 return fmt.Errorf("whicherrs wants an expression; got %s",
60                         astutil.NodeDescription(qpos.path[0]))
61         }
62         var expr ast.Expr
63         var obj types.Object
64         switch n := path[0].(type) {
65         case *ast.ValueSpec:
66                 // ambiguous ValueSpec containing multiple names
67                 return fmt.Errorf("multiple value specification")
68         case *ast.Ident:
69                 obj = qpos.info.ObjectOf(n)
70                 expr = n
71         case ast.Expr:
72                 expr = n
73         default:
74                 return fmt.Errorf("unexpected AST for expr: %T", n)
75         }
76
77         typ := qpos.info.TypeOf(expr)
78         if !types.Identical(typ, builtinErrorType) {
79                 return fmt.Errorf("selection is not an expression of type 'error'")
80         }
81         // Determine the ssa.Value for the expression.
82         var value ssa.Value
83         if obj != nil {
84                 // def/ref of func/var object
85                 value, _, err = ssaValueForIdent(prog, qpos.info, obj, path)
86         } else {
87                 value, _, err = ssaValueForExpr(prog, qpos.info, path)
88         }
89         if err != nil {
90                 return err // e.g. trivially dead code
91         }
92
93         // Defer SSA construction till after errors are reported.
94         prog.Build()
95
96         globals := findVisibleErrs(prog, qpos)
97         constants := findVisibleConsts(prog, qpos)
98
99         res := &whicherrsResult{
100                 qpos:   qpos,
101                 errpos: expr.Pos(),
102         }
103
104         // TODO(adonovan): the following code is heavily duplicated
105         // w.r.t.  "pointsto".  Refactor?
106
107         // Find the instruction which initialized the
108         // global error. If more than one instruction has stored to the global
109         // remove the global from the set of values that we want to query.
110         allFuncs := ssautil.AllFunctions(prog)
111         for fn := range allFuncs {
112                 for _, b := range fn.Blocks {
113                         for _, instr := range b.Instrs {
114                                 store, ok := instr.(*ssa.Store)
115                                 if !ok {
116                                         continue
117                                 }
118                                 gval, ok := store.Addr.(*ssa.Global)
119                                 if !ok {
120                                         continue
121                                 }
122                                 gbl, ok := globals[gval]
123                                 if !ok {
124                                         continue
125                                 }
126                                 // we already found a store to this global
127                                 // The normal error define is just one store in the init
128                                 // so we just remove this global from the set we want to query
129                                 if gbl != nil {
130                                         delete(globals, gval)
131                                 }
132                                 globals[gval] = store.Val
133                         }
134                 }
135         }
136
137         ptaConfig.AddQuery(value)
138         for _, v := range globals {
139                 ptaConfig.AddQuery(v)
140         }
141
142         ptares := ptrAnalysis(ptaConfig)
143         valueptr := ptares.Queries[value]
144         if valueptr == (pointer.Pointer{}) {
145                 return fmt.Errorf("pointer analysis did not find expression (dead code?)")
146         }
147         for g, v := range globals {
148                 ptr, ok := ptares.Queries[v]
149                 if !ok {
150                         continue
151                 }
152                 if !ptr.MayAlias(valueptr) {
153                         continue
154                 }
155                 res.globals = append(res.globals, g)
156         }
157         pts := valueptr.PointsTo()
158         dedup := make(map[*ssa.NamedConst]bool)
159         for _, label := range pts.Labels() {
160                 // These values are either MakeInterfaces or reflect
161                 // generated interfaces. For the purposes of this
162                 // analysis, we don't care about reflect generated ones
163                 makeiface, ok := label.Value().(*ssa.MakeInterface)
164                 if !ok {
165                         continue
166                 }
167                 constval, ok := makeiface.X.(*ssa.Const)
168                 if !ok {
169                         continue
170                 }
171                 c := constants[*constval]
172                 if c != nil && !dedup[c] {
173                         dedup[c] = true
174                         res.consts = append(res.consts, c)
175                 }
176         }
177         concs := pts.DynamicTypes()
178         concs.Iterate(func(conc types.Type, _ interface{}) {
179                 // go/types is a bit annoying here.
180                 // We want to find all the types that we can
181                 // typeswitch or assert to. This means finding out
182                 // if the type pointed to can be seen by us.
183                 //
184                 // For the purposes of this analysis, we care only about
185                 // TypeNames of Named or pointer-to-Named types.
186                 // We ignore other types (e.g. structs) that implement error.
187                 var name *types.TypeName
188                 switch t := conc.(type) {
189                 case *types.Pointer:
190                         named, ok := t.Elem().(*types.Named)
191                         if !ok {
192                                 return
193                         }
194                         name = named.Obj()
195                 case *types.Named:
196                         name = t.Obj()
197                 default:
198                         return
199                 }
200                 if !isAccessibleFrom(name, qpos.info.Pkg) {
201                         return
202                 }
203                 res.types = append(res.types, &errorType{conc, name})
204         })
205         sort.Sort(membersByPosAndString(res.globals))
206         sort.Sort(membersByPosAndString(res.consts))
207         sort.Sort(sorterrorType(res.types))
208
209         q.Output(lprog.Fset, res)
210         return nil
211 }
212
213 // findVisibleErrs returns a mapping from each package-level variable of type "error" to nil.
214 func findVisibleErrs(prog *ssa.Program, qpos *queryPos) map[*ssa.Global]ssa.Value {
215         globals := make(map[*ssa.Global]ssa.Value)
216         for _, pkg := range prog.AllPackages() {
217                 for _, mem := range pkg.Members {
218                         gbl, ok := mem.(*ssa.Global)
219                         if !ok {
220                                 continue
221                         }
222                         gbltype := gbl.Type()
223                         // globals are always pointers
224                         if !types.Identical(deref(gbltype), builtinErrorType) {
225                                 continue
226                         }
227                         if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) {
228                                 continue
229                         }
230                         globals[gbl] = nil
231                 }
232         }
233         return globals
234 }
235
236 // findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil.
237 func findVisibleConsts(prog *ssa.Program, qpos *queryPos) map[ssa.Const]*ssa.NamedConst {
238         constants := make(map[ssa.Const]*ssa.NamedConst)
239         for _, pkg := range prog.AllPackages() {
240                 for _, mem := range pkg.Members {
241                         obj, ok := mem.(*ssa.NamedConst)
242                         if !ok {
243                                 continue
244                         }
245                         consttype := obj.Type()
246                         if !types.AssignableTo(consttype, builtinErrorType) {
247                                 continue
248                         }
249                         if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) {
250                                 continue
251                         }
252                         constants[*obj.Value] = obj
253                 }
254         }
255
256         return constants
257 }
258
259 type membersByPosAndString []ssa.Member
260
261 func (a membersByPosAndString) Len() int { return len(a) }
262 func (a membersByPosAndString) Less(i, j int) bool {
263         cmp := a[i].Pos() - a[j].Pos()
264         return cmp < 0 || cmp == 0 && a[i].String() < a[j].String()
265 }
266 func (a membersByPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
267
268 type sorterrorType []*errorType
269
270 func (a sorterrorType) Len() int { return len(a) }
271 func (a sorterrorType) Less(i, j int) bool {
272         cmp := a[i].obj.Pos() - a[j].obj.Pos()
273         return cmp < 0 || cmp == 0 && a[i].typ.String() < a[j].typ.String()
274 }
275 func (a sorterrorType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
276
277 type errorType struct {
278         typ types.Type      // concrete type N or *N that implements error
279         obj *types.TypeName // the named type N
280 }
281
282 type whicherrsResult struct {
283         qpos    *queryPos
284         errpos  token.Pos
285         globals []ssa.Member
286         consts  []ssa.Member
287         types   []*errorType
288 }
289
290 func (r *whicherrsResult) PrintPlain(printf printfFunc) {
291         if len(r.globals) > 0 {
292                 printf(r.qpos, "this error may point to these globals:")
293                 for _, g := range r.globals {
294                         printf(g.Pos(), "\t%s", g.RelString(r.qpos.info.Pkg))
295                 }
296         }
297         if len(r.consts) > 0 {
298                 printf(r.qpos, "this error may contain these constants:")
299                 for _, c := range r.consts {
300                         printf(c.Pos(), "\t%s", c.RelString(r.qpos.info.Pkg))
301                 }
302         }
303         if len(r.types) > 0 {
304                 printf(r.qpos, "this error may contain these dynamic types:")
305                 for _, t := range r.types {
306                         printf(t.obj.Pos(), "\t%s", r.qpos.typeString(t.typ))
307                 }
308         }
309 }
310
311 func (r *whicherrsResult) JSON(fset *token.FileSet) []byte {
312         we := &serial.WhichErrs{}
313         we.ErrPos = fset.Position(r.errpos).String()
314         for _, g := range r.globals {
315                 we.Globals = append(we.Globals, fset.Position(g.Pos()).String())
316         }
317         for _, c := range r.consts {
318                 we.Constants = append(we.Constants, fset.Position(c.Pos()).String())
319         }
320         for _, t := range r.types {
321                 var et serial.WhichErrsType
322                 et.Type = r.qpos.typeString(t.typ)
323                 et.Position = fset.Position(t.obj.Pos()).String()
324                 we.Types = append(we.Types, et)
325         }
326         return toJSON(we)
327 }