9 "honnef.co/go/tools/go/ir"
10 "honnef.co/go/tools/go/types/typeutil"
11 "honnef.co/go/tools/internal/passes/buildir"
13 "golang.org/x/tools/go/analysis"
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
19 type neverReturnsNilFact struct {
23 func (*neverReturnsNilFact) AFact() {}
24 func (fact *neverReturnsNilFact) String() string {
25 return fmt.Sprintf("never returns nil: %v", fact.Rets)
29 m map[*types.Func][]neverNilness
32 var Analysis = &analysis.Analyzer{
34 Doc: "Annotates return values that will never be nil (typed or untyped)",
36 Requires: []*analysis.Analyzer{buildir.Analyzer},
37 FactTypes: []analysis.Fact{(*neverReturnsNilFact)(nil)},
38 ResultType: reflect.TypeOf((*Result)(nil)),
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
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()) {
52 if len(r.m[fn]) == 0 {
57 return v != neverNil, v == onlyGlobal
60 func run(pass *analysis.Pass) (interface{}, error) {
61 seen := map[*ir.Function]struct{}{}
63 m: map[*types.Func][]neverNilness{},
65 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
69 for _, fact := range pass.AllObjectFacts() {
70 out.m[fact.Object.(*types.Func)] = fact.Fact.(*neverReturnsNilFact).Rets
76 type neverNilness uint8
79 neverNil neverNilness = 1
80 onlyGlobal neverNilness = 2
81 nilly neverNilness = 3
84 func (n neverNilness) String() string {
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
102 if fact := new(neverReturnsNilFact); pass.ImportObjectFact(fn.Object(), fact) {
105 if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
108 if fn.Blocks == nil {
111 if _, ok := seenFns[fn]; ok {
116 seenFns[fn] = struct{}{}
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})
126 seen := map[ir.Value]struct{}{}
128 var mightReturnNil func(v ir.Value) neverNilness
129 mightReturnNil = func(v ir.Value) neverNilness {
130 if _, ok := seen[v]; ok {
134 if !typeutil.IsPointerLike(v.Type()) {
138 switch v := v.(type) {
139 case *ir.MakeInterface:
140 return mightReturnNil(v.X)
142 return mightReturnNil(v.X)
144 return mightReturnNil(v.X)
147 for _, e := range v.Edges {
148 if n := mightReturnNil(e); n > ret {
154 switch d := v.Tuple.(type) {
156 if callee := d.Call.StaticCallee(); callee != nil {
157 ret := impl(pass, callee, seenFns)
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.
170 panic(fmt.Sprintf("internal error: unhandled type %T", d))
173 if callee := v.Call.StaticCallee(); callee != nil {
174 ret := impl(pass, callee, seenFns)
182 case *ir.BinOp, *ir.UnOp, *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr, *ir.Global, *ir.MakeSlice, *ir.MakeClosure, *ir.Function, *ir.MakeMap, *ir.MakeChan:
185 iff, ok := v.From.Control().(*ir.If)
189 binop, ok := iff.Cond.(*ir.BinOp)
193 isNil := func(v ir.Value) bool {
194 k, ok := v.(*ir.Const)
198 return k.Value == nil
200 if binop.X == v.X && isNil(binop.Y) || binop.Y == v.X && isNil(binop.X) {
202 if v.From.Succs[0] != v.Block() {
203 // we're in the false branch, negate op
210 panic(fmt.Sprintf("internal error: unhandled token %v", op))
219 panic(fmt.Sprintf("internal error: unhandled token %v", op))
224 return mightReturnNil(v.X)
226 if _, ok := v.X.(*ir.Global); ok {
230 case *ir.TypeAssert, *ir.ChangeInterface, *ir.Field, *ir.Const, *ir.Index, *ir.MapLookup, *ir.Parameter, *ir.Recv, *ir.TypeSwitch:
233 panic(fmt.Sprintf("internal error: unhandled type %T", v))
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)