.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / cmd / guru / whicherrs.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/cmd/guru/whicherrs.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/cmd/guru/whicherrs.go
new file mode 100644 (file)
index 0000000..3a81bf5
--- /dev/null
@@ -0,0 +1,327 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+       "fmt"
+       "go/ast"
+       "go/token"
+       "go/types"
+       "sort"
+
+       "golang.org/x/tools/cmd/guru/serial"
+       "golang.org/x/tools/go/ast/astutil"
+       "golang.org/x/tools/go/loader"
+       "golang.org/x/tools/go/pointer"
+       "golang.org/x/tools/go/ssa"
+       "golang.org/x/tools/go/ssa/ssautil"
+)
+
+var builtinErrorType = types.Universe.Lookup("error").Type()
+
+// whicherrs takes an position to an error and tries to find all types, constants
+// and global value which a given error can point to and which can be checked from the
+// scope where the error lives.
+// In short, it returns a list of things that can be checked against in order to handle
+// an error properly.
+//
+// TODO(dmorsing): figure out if fields in errors like *os.PathError.Err
+// can be queried recursively somehow.
+func whicherrs(q *Query) error {
+       lconf := loader.Config{Build: q.Build}
+
+       if err := setPTAScope(&lconf, q.Scope); err != nil {
+               return err
+       }
+
+       // Load/parse/type-check the program.
+       lprog, err := loadWithSoftErrors(&lconf)
+       if err != nil {
+               return err
+       }
+
+       qpos, err := parseQueryPos(lprog, q.Pos, true) // needs exact pos
+       if err != nil {
+               return err
+       }
+
+       prog := ssautil.CreateProgram(lprog, ssa.GlobalDebug)
+
+       ptaConfig, err := setupPTA(prog, lprog, q.PTALog, q.Reflection)
+       if err != nil {
+               return err
+       }
+
+       path, action := findInterestingNode(qpos.info, qpos.path)
+       if action != actionExpr {
+               return fmt.Errorf("whicherrs wants an expression; got %s",
+                       astutil.NodeDescription(qpos.path[0]))
+       }
+       var expr ast.Expr
+       var obj types.Object
+       switch n := path[0].(type) {
+       case *ast.ValueSpec:
+               // ambiguous ValueSpec containing multiple names
+               return fmt.Errorf("multiple value specification")
+       case *ast.Ident:
+               obj = qpos.info.ObjectOf(n)
+               expr = n
+       case ast.Expr:
+               expr = n
+       default:
+               return fmt.Errorf("unexpected AST for expr: %T", n)
+       }
+
+       typ := qpos.info.TypeOf(expr)
+       if !types.Identical(typ, builtinErrorType) {
+               return fmt.Errorf("selection is not an expression of type 'error'")
+       }
+       // Determine the ssa.Value for the expression.
+       var value ssa.Value
+       if obj != nil {
+               // def/ref of func/var object
+               value, _, err = ssaValueForIdent(prog, qpos.info, obj, path)
+       } else {
+               value, _, err = ssaValueForExpr(prog, qpos.info, path)
+       }
+       if err != nil {
+               return err // e.g. trivially dead code
+       }
+
+       // Defer SSA construction till after errors are reported.
+       prog.Build()
+
+       globals := findVisibleErrs(prog, qpos)
+       constants := findVisibleConsts(prog, qpos)
+
+       res := &whicherrsResult{
+               qpos:   qpos,
+               errpos: expr.Pos(),
+       }
+
+       // TODO(adonovan): the following code is heavily duplicated
+       // w.r.t.  "pointsto".  Refactor?
+
+       // Find the instruction which initialized the
+       // global error. If more than one instruction has stored to the global
+       // remove the global from the set of values that we want to query.
+       allFuncs := ssautil.AllFunctions(prog)
+       for fn := range allFuncs {
+               for _, b := range fn.Blocks {
+                       for _, instr := range b.Instrs {
+                               store, ok := instr.(*ssa.Store)
+                               if !ok {
+                                       continue
+                               }
+                               gval, ok := store.Addr.(*ssa.Global)
+                               if !ok {
+                                       continue
+                               }
+                               gbl, ok := globals[gval]
+                               if !ok {
+                                       continue
+                               }
+                               // we already found a store to this global
+                               // The normal error define is just one store in the init
+                               // so we just remove this global from the set we want to query
+                               if gbl != nil {
+                                       delete(globals, gval)
+                               }
+                               globals[gval] = store.Val
+                       }
+               }
+       }
+
+       ptaConfig.AddQuery(value)
+       for _, v := range globals {
+               ptaConfig.AddQuery(v)
+       }
+
+       ptares := ptrAnalysis(ptaConfig)
+       valueptr := ptares.Queries[value]
+       if valueptr == (pointer.Pointer{}) {
+               return fmt.Errorf("pointer analysis did not find expression (dead code?)")
+       }
+       for g, v := range globals {
+               ptr, ok := ptares.Queries[v]
+               if !ok {
+                       continue
+               }
+               if !ptr.MayAlias(valueptr) {
+                       continue
+               }
+               res.globals = append(res.globals, g)
+       }
+       pts := valueptr.PointsTo()
+       dedup := make(map[*ssa.NamedConst]bool)
+       for _, label := range pts.Labels() {
+               // These values are either MakeInterfaces or reflect
+               // generated interfaces. For the purposes of this
+               // analysis, we don't care about reflect generated ones
+               makeiface, ok := label.Value().(*ssa.MakeInterface)
+               if !ok {
+                       continue
+               }
+               constval, ok := makeiface.X.(*ssa.Const)
+               if !ok {
+                       continue
+               }
+               c := constants[*constval]
+               if c != nil && !dedup[c] {
+                       dedup[c] = true
+                       res.consts = append(res.consts, c)
+               }
+       }
+       concs := pts.DynamicTypes()
+       concs.Iterate(func(conc types.Type, _ interface{}) {
+               // go/types is a bit annoying here.
+               // We want to find all the types that we can
+               // typeswitch or assert to. This means finding out
+               // if the type pointed to can be seen by us.
+               //
+               // For the purposes of this analysis, we care only about
+               // TypeNames of Named or pointer-to-Named types.
+               // We ignore other types (e.g. structs) that implement error.
+               var name *types.TypeName
+               switch t := conc.(type) {
+               case *types.Pointer:
+                       named, ok := t.Elem().(*types.Named)
+                       if !ok {
+                               return
+                       }
+                       name = named.Obj()
+               case *types.Named:
+                       name = t.Obj()
+               default:
+                       return
+               }
+               if !isAccessibleFrom(name, qpos.info.Pkg) {
+                       return
+               }
+               res.types = append(res.types, &errorType{conc, name})
+       })
+       sort.Sort(membersByPosAndString(res.globals))
+       sort.Sort(membersByPosAndString(res.consts))
+       sort.Sort(sorterrorType(res.types))
+
+       q.Output(lprog.Fset, res)
+       return nil
+}
+
+// findVisibleErrs returns a mapping from each package-level variable of type "error" to nil.
+func findVisibleErrs(prog *ssa.Program, qpos *queryPos) map[*ssa.Global]ssa.Value {
+       globals := make(map[*ssa.Global]ssa.Value)
+       for _, pkg := range prog.AllPackages() {
+               for _, mem := range pkg.Members {
+                       gbl, ok := mem.(*ssa.Global)
+                       if !ok {
+                               continue
+                       }
+                       gbltype := gbl.Type()
+                       // globals are always pointers
+                       if !types.Identical(deref(gbltype), builtinErrorType) {
+                               continue
+                       }
+                       if !isAccessibleFrom(gbl.Object(), qpos.info.Pkg) {
+                               continue
+                       }
+                       globals[gbl] = nil
+               }
+       }
+       return globals
+}
+
+// findVisibleConsts returns a mapping from each package-level constant assignable to type "error", to nil.
+func findVisibleConsts(prog *ssa.Program, qpos *queryPos) map[ssa.Const]*ssa.NamedConst {
+       constants := make(map[ssa.Const]*ssa.NamedConst)
+       for _, pkg := range prog.AllPackages() {
+               for _, mem := range pkg.Members {
+                       obj, ok := mem.(*ssa.NamedConst)
+                       if !ok {
+                               continue
+                       }
+                       consttype := obj.Type()
+                       if !types.AssignableTo(consttype, builtinErrorType) {
+                               continue
+                       }
+                       if !isAccessibleFrom(obj.Object(), qpos.info.Pkg) {
+                               continue
+                       }
+                       constants[*obj.Value] = obj
+               }
+       }
+
+       return constants
+}
+
+type membersByPosAndString []ssa.Member
+
+func (a membersByPosAndString) Len() int { return len(a) }
+func (a membersByPosAndString) Less(i, j int) bool {
+       cmp := a[i].Pos() - a[j].Pos()
+       return cmp < 0 || cmp == 0 && a[i].String() < a[j].String()
+}
+func (a membersByPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type sorterrorType []*errorType
+
+func (a sorterrorType) Len() int { return len(a) }
+func (a sorterrorType) Less(i, j int) bool {
+       cmp := a[i].obj.Pos() - a[j].obj.Pos()
+       return cmp < 0 || cmp == 0 && a[i].typ.String() < a[j].typ.String()
+}
+func (a sorterrorType) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type errorType struct {
+       typ types.Type      // concrete type N or *N that implements error
+       obj *types.TypeName // the named type N
+}
+
+type whicherrsResult struct {
+       qpos    *queryPos
+       errpos  token.Pos
+       globals []ssa.Member
+       consts  []ssa.Member
+       types   []*errorType
+}
+
+func (r *whicherrsResult) PrintPlain(printf printfFunc) {
+       if len(r.globals) > 0 {
+               printf(r.qpos, "this error may point to these globals:")
+               for _, g := range r.globals {
+                       printf(g.Pos(), "\t%s", g.RelString(r.qpos.info.Pkg))
+               }
+       }
+       if len(r.consts) > 0 {
+               printf(r.qpos, "this error may contain these constants:")
+               for _, c := range r.consts {
+                       printf(c.Pos(), "\t%s", c.RelString(r.qpos.info.Pkg))
+               }
+       }
+       if len(r.types) > 0 {
+               printf(r.qpos, "this error may contain these dynamic types:")
+               for _, t := range r.types {
+                       printf(t.obj.Pos(), "\t%s", r.qpos.typeString(t.typ))
+               }
+       }
+}
+
+func (r *whicherrsResult) JSON(fset *token.FileSet) []byte {
+       we := &serial.WhichErrs{}
+       we.ErrPos = fset.Position(r.errpos).String()
+       for _, g := range r.globals {
+               we.Globals = append(we.Globals, fset.Position(g.Pos()).String())
+       }
+       for _, c := range r.consts {
+               we.Constants = append(we.Constants, fset.Position(c.Pos()).String())
+       }
+       for _, t := range r.types {
+               var et serial.WhichErrsType
+               et.Type = r.qpos.typeString(t.typ)
+               et.Position = fset.Position(t.obj.Pos()).String()
+               we.Types = append(we.Types, et)
+       }
+       return toJSON(we)
+}