.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
1 package nilness
2
3 import (
4         "fmt"
5         "go/token"
6         "go/types"
7         "reflect"
8
9         "honnef.co/go/tools/go/ir"
10         "honnef.co/go/tools/go/types/typeutil"
11         "honnef.co/go/tools/internal/passes/buildir"
12
13         "golang.org/x/tools/go/analysis"
14 )
15
16 // neverReturnsNilFact denotes that a function's return value will never
17 // be nil (typed or untyped). The analysis errs on the side of false
18 // negatives.
19 type neverReturnsNilFact struct {
20         Rets []neverNilness
21 }
22
23 func (*neverReturnsNilFact) AFact() {}
24 func (fact *neverReturnsNilFact) String() string {
25         return fmt.Sprintf("never returns nil: %v", fact.Rets)
26 }
27
28 type Result struct {
29         m map[*types.Func][]neverNilness
30 }
31
32 var Analysis = &analysis.Analyzer{
33         Name:       "nilness",
34         Doc:        "Annotates return values that will never be nil (typed or untyped)",
35         Run:        run,
36         Requires:   []*analysis.Analyzer{buildir.Analyzer},
37         FactTypes:  []analysis.Fact{(*neverReturnsNilFact)(nil)},
38         ResultType: reflect.TypeOf((*Result)(nil)),
39 }
40
41 // MayReturnNil reports whether the ret's return value of fn might be
42 // a typed or untyped nil value. The value of ret is zero-based. When
43 // globalOnly is true, the only possible nil values are global
44 // variables.
45 //
46 // The analysis has false positives: MayReturnNil can incorrectly
47 // report true, but never incorrectly reports false.
48 func (r *Result) MayReturnNil(fn *types.Func, ret int) (yes bool, globalOnly bool) {
49         if !typeutil.IsPointerLike(fn.Type().(*types.Signature).Results().At(ret).Type()) {
50                 return false, false
51         }
52         if len(r.m[fn]) == 0 {
53                 return true, false
54         }
55
56         v := r.m[fn][ret]
57         return v != neverNil, v == onlyGlobal
58 }
59
60 func run(pass *analysis.Pass) (interface{}, error) {
61         seen := map[*ir.Function]struct{}{}
62         out := &Result{
63                 m: map[*types.Func][]neverNilness{},
64         }
65         for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
66                 impl(pass, fn, seen)
67         }
68
69         for _, fact := range pass.AllObjectFacts() {
70                 out.m[fact.Object.(*types.Func)] = fact.Fact.(*neverReturnsNilFact).Rets
71         }
72
73         return out, nil
74 }
75
76 type neverNilness uint8
77
78 const (
79         neverNil   neverNilness = 1
80         onlyGlobal neverNilness = 2
81         nilly      neverNilness = 3
82 )
83
84 func (n neverNilness) String() string {
85         switch n {
86         case neverNil:
87                 return "never"
88         case onlyGlobal:
89                 return "global"
90         case nilly:
91                 return "nil"
92         default:
93                 return "BUG"
94         }
95 }
96
97 func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) (out []neverNilness) {
98         if fn.Object() == nil {
99                 // TODO(dh): support closures
100                 return nil
101         }
102         if fact := new(neverReturnsNilFact); pass.ImportObjectFact(fn.Object(), fact) {
103                 return fact.Rets
104         }
105         if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
106                 return nil
107         }
108         if fn.Blocks == nil {
109                 return nil
110         }
111         if _, ok := seenFns[fn]; ok {
112                 // break recursion
113                 return nil
114         }
115
116         seenFns[fn] = struct{}{}
117         defer func() {
118                 for i, v := range out {
119                         if typeutil.IsPointerLike(fn.Signature.Results().At(i).Type()) && v != nilly {
120                                 pass.ExportObjectFact(fn.Object(), &neverReturnsNilFact{out})
121                                 break
122                         }
123                 }
124         }()
125
126         seen := map[ir.Value]struct{}{}
127
128         var mightReturnNil func(v ir.Value) neverNilness
129         mightReturnNil = func(v ir.Value) neverNilness {
130                 if _, ok := seen[v]; ok {
131                         // break cycle
132                         return nilly
133                 }
134                 if !typeutil.IsPointerLike(v.Type()) {
135                         return neverNil
136                 }
137                 seen[v] = struct{}{}
138                 switch v := v.(type) {
139                 case *ir.MakeInterface:
140                         return mightReturnNil(v.X)
141                 case *ir.Convert:
142                         return mightReturnNil(v.X)
143                 case *ir.Slice:
144                         return mightReturnNil(v.X)
145                 case *ir.Phi:
146                         ret := neverNil
147                         for _, e := range v.Edges {
148                                 if n := mightReturnNil(e); n > ret {
149                                         ret = n
150                                 }
151                         }
152                         return ret
153                 case *ir.Extract:
154                         switch d := v.Tuple.(type) {
155                         case *ir.Call:
156                                 if callee := d.Call.StaticCallee(); callee != nil {
157                                         ret := impl(pass, callee, seenFns)
158                                         if len(ret) == 0 {
159                                                 return nilly
160                                         }
161                                         return ret[v.Index]
162                                 } else {
163                                         return nilly
164                                 }
165                         case *ir.TypeAssert, *ir.Next, *ir.Select, *ir.MapLookup, *ir.TypeSwitch, *ir.Recv:
166                                 // we don't need to look at the Extract's index
167                                 // because we've already checked its type.
168                                 return nilly
169                         default:
170                                 panic(fmt.Sprintf("internal error: unhandled type %T", d))
171                         }
172                 case *ir.Call:
173                         if callee := v.Call.StaticCallee(); callee != nil {
174                                 ret := impl(pass, callee, seenFns)
175                                 if len(ret) == 0 {
176                                         return nilly
177                                 }
178                                 return ret[0]
179                         } else {
180                                 return nilly
181                         }
182                 case *ir.BinOp, *ir.UnOp, *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr, *ir.Global, *ir.MakeSlice, *ir.MakeClosure, *ir.Function, *ir.MakeMap, *ir.MakeChan:
183                         return neverNil
184                 case *ir.Sigma:
185                         iff, ok := v.From.Control().(*ir.If)
186                         if !ok {
187                                 return nilly
188                         }
189                         binop, ok := iff.Cond.(*ir.BinOp)
190                         if !ok {
191                                 return nilly
192                         }
193                         isNil := func(v ir.Value) bool {
194                                 k, ok := v.(*ir.Const)
195                                 if !ok {
196                                         return false
197                                 }
198                                 return k.Value == nil
199                         }
200                         if binop.X == v.X && isNil(binop.Y) || binop.Y == v.X && isNil(binop.X) {
201                                 op := binop.Op
202                                 if v.From.Succs[0] != v.Block() {
203                                         // we're in the false branch, negate op
204                                         switch op {
205                                         case token.EQL:
206                                                 op = token.NEQ
207                                         case token.NEQ:
208                                                 op = token.EQL
209                                         default:
210                                                 panic(fmt.Sprintf("internal error: unhandled token %v", op))
211                                         }
212                                 }
213                                 switch op {
214                                 case token.EQL:
215                                         return nilly
216                                 case token.NEQ:
217                                         return neverNil
218                                 default:
219                                         panic(fmt.Sprintf("internal error: unhandled token %v", op))
220                                 }
221                         }
222                         return nilly
223                 case *ir.ChangeType:
224                         return mightReturnNil(v.X)
225                 case *ir.Load:
226                         if _, ok := v.X.(*ir.Global); ok {
227                                 return onlyGlobal
228                         }
229                         return nilly
230                 case *ir.TypeAssert, *ir.ChangeInterface, *ir.Field, *ir.Const, *ir.Index, *ir.MapLookup, *ir.Parameter, *ir.Recv, *ir.TypeSwitch:
231                         return nilly
232                 default:
233                         panic(fmt.Sprintf("internal error: unhandled type %T", v))
234                 }
235         }
236         ret := fn.Exit.Control().(*ir.Return)
237         out = make([]neverNilness, len(ret.Results))
238         for i, v := range ret.Results {
239                 out[i] = mightReturnNil(v)
240         }
241         return out
242 }