.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / analysis / facts / nilness / nilness.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/honnef.co/go/tools@v0.1.1/analysis/facts/nilness/nilness.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/honnef.co/go/tools@v0.1.1/analysis/facts/nilness/nilness.go
new file mode 100644 (file)
index 0000000..973f1f5
--- /dev/null
@@ -0,0 +1,242 @@
+package nilness
+
+import (
+       "fmt"
+       "go/token"
+       "go/types"
+       "reflect"
+
+       "honnef.co/go/tools/go/ir"
+       "honnef.co/go/tools/go/types/typeutil"
+       "honnef.co/go/tools/internal/passes/buildir"
+
+       "golang.org/x/tools/go/analysis"
+)
+
+// neverReturnsNilFact denotes that a function's return value will never
+// be nil (typed or untyped). The analysis errs on the side of false
+// negatives.
+type neverReturnsNilFact struct {
+       Rets []neverNilness
+}
+
+func (*neverReturnsNilFact) AFact() {}
+func (fact *neverReturnsNilFact) String() string {
+       return fmt.Sprintf("never returns nil: %v", fact.Rets)
+}
+
+type Result struct {
+       m map[*types.Func][]neverNilness
+}
+
+var Analysis = &analysis.Analyzer{
+       Name:       "nilness",
+       Doc:        "Annotates return values that will never be nil (typed or untyped)",
+       Run:        run,
+       Requires:   []*analysis.Analyzer{buildir.Analyzer},
+       FactTypes:  []analysis.Fact{(*neverReturnsNilFact)(nil)},
+       ResultType: reflect.TypeOf((*Result)(nil)),
+}
+
+// MayReturnNil reports whether the ret's return value of fn might be
+// a typed or untyped nil value. The value of ret is zero-based. When
+// globalOnly is true, the only possible nil values are global
+// variables.
+//
+// The analysis has false positives: MayReturnNil can incorrectly
+// report true, but never incorrectly reports false.
+func (r *Result) MayReturnNil(fn *types.Func, ret int) (yes bool, globalOnly bool) {
+       if !typeutil.IsPointerLike(fn.Type().(*types.Signature).Results().At(ret).Type()) {
+               return false, false
+       }
+       if len(r.m[fn]) == 0 {
+               return true, false
+       }
+
+       v := r.m[fn][ret]
+       return v != neverNil, v == onlyGlobal
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+       seen := map[*ir.Function]struct{}{}
+       out := &Result{
+               m: map[*types.Func][]neverNilness{},
+       }
+       for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
+               impl(pass, fn, seen)
+       }
+
+       for _, fact := range pass.AllObjectFacts() {
+               out.m[fact.Object.(*types.Func)] = fact.Fact.(*neverReturnsNilFact).Rets
+       }
+
+       return out, nil
+}
+
+type neverNilness uint8
+
+const (
+       neverNil   neverNilness = 1
+       onlyGlobal neverNilness = 2
+       nilly      neverNilness = 3
+)
+
+func (n neverNilness) String() string {
+       switch n {
+       case neverNil:
+               return "never"
+       case onlyGlobal:
+               return "global"
+       case nilly:
+               return "nil"
+       default:
+               return "BUG"
+       }
+}
+
+func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) (out []neverNilness) {
+       if fn.Object() == nil {
+               // TODO(dh): support closures
+               return nil
+       }
+       if fact := new(neverReturnsNilFact); pass.ImportObjectFact(fn.Object(), fact) {
+               return fact.Rets
+       }
+       if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
+               return nil
+       }
+       if fn.Blocks == nil {
+               return nil
+       }
+       if _, ok := seenFns[fn]; ok {
+               // break recursion
+               return nil
+       }
+
+       seenFns[fn] = struct{}{}
+       defer func() {
+               for i, v := range out {
+                       if typeutil.IsPointerLike(fn.Signature.Results().At(i).Type()) && v != nilly {
+                               pass.ExportObjectFact(fn.Object(), &neverReturnsNilFact{out})
+                               break
+                       }
+               }
+       }()
+
+       seen := map[ir.Value]struct{}{}
+
+       var mightReturnNil func(v ir.Value) neverNilness
+       mightReturnNil = func(v ir.Value) neverNilness {
+               if _, ok := seen[v]; ok {
+                       // break cycle
+                       return nilly
+               }
+               if !typeutil.IsPointerLike(v.Type()) {
+                       return neverNil
+               }
+               seen[v] = struct{}{}
+               switch v := v.(type) {
+               case *ir.MakeInterface:
+                       return mightReturnNil(v.X)
+               case *ir.Convert:
+                       return mightReturnNil(v.X)
+               case *ir.Slice:
+                       return mightReturnNil(v.X)
+               case *ir.Phi:
+                       ret := neverNil
+                       for _, e := range v.Edges {
+                               if n := mightReturnNil(e); n > ret {
+                                       ret = n
+                               }
+                       }
+                       return ret
+               case *ir.Extract:
+                       switch d := v.Tuple.(type) {
+                       case *ir.Call:
+                               if callee := d.Call.StaticCallee(); callee != nil {
+                                       ret := impl(pass, callee, seenFns)
+                                       if len(ret) == 0 {
+                                               return nilly
+                                       }
+                                       return ret[v.Index]
+                               } else {
+                                       return nilly
+                               }
+                       case *ir.TypeAssert, *ir.Next, *ir.Select, *ir.MapLookup, *ir.TypeSwitch, *ir.Recv:
+                               // we don't need to look at the Extract's index
+                               // because we've already checked its type.
+                               return nilly
+                       default:
+                               panic(fmt.Sprintf("internal error: unhandled type %T", d))
+                       }
+               case *ir.Call:
+                       if callee := v.Call.StaticCallee(); callee != nil {
+                               ret := impl(pass, callee, seenFns)
+                               if len(ret) == 0 {
+                                       return nilly
+                               }
+                               return ret[0]
+                       } else {
+                               return nilly
+                       }
+               case *ir.BinOp, *ir.UnOp, *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr, *ir.Global, *ir.MakeSlice, *ir.MakeClosure, *ir.Function, *ir.MakeMap, *ir.MakeChan:
+                       return neverNil
+               case *ir.Sigma:
+                       iff, ok := v.From.Control().(*ir.If)
+                       if !ok {
+                               return nilly
+                       }
+                       binop, ok := iff.Cond.(*ir.BinOp)
+                       if !ok {
+                               return nilly
+                       }
+                       isNil := func(v ir.Value) bool {
+                               k, ok := v.(*ir.Const)
+                               if !ok {
+                                       return false
+                               }
+                               return k.Value == nil
+                       }
+                       if binop.X == v.X && isNil(binop.Y) || binop.Y == v.X && isNil(binop.X) {
+                               op := binop.Op
+                               if v.From.Succs[0] != v.Block() {
+                                       // we're in the false branch, negate op
+                                       switch op {
+                                       case token.EQL:
+                                               op = token.NEQ
+                                       case token.NEQ:
+                                               op = token.EQL
+                                       default:
+                                               panic(fmt.Sprintf("internal error: unhandled token %v", op))
+                                       }
+                               }
+                               switch op {
+                               case token.EQL:
+                                       return nilly
+                               case token.NEQ:
+                                       return neverNil
+                               default:
+                                       panic(fmt.Sprintf("internal error: unhandled token %v", op))
+                               }
+                       }
+                       return nilly
+               case *ir.ChangeType:
+                       return mightReturnNil(v.X)
+               case *ir.Load:
+                       if _, ok := v.X.(*ir.Global); ok {
+                               return onlyGlobal
+                       }
+                       return nilly
+               case *ir.TypeAssert, *ir.ChangeInterface, *ir.Field, *ir.Const, *ir.Index, *ir.MapLookup, *ir.Parameter, *ir.Recv, *ir.TypeSwitch:
+                       return nilly
+               default:
+                       panic(fmt.Sprintf("internal error: unhandled type %T", v))
+               }
+       }
+       ret := fn.Exit.Control().(*ir.Return)
+       out = make([]neverNilness, len(ret.Results))
+       for i, v := range ret.Results {
+               out[i] = mightReturnNil(v)
+       }
+       return out
+}