GoPLS Viewer

Home|gopls/go/analysis/passes/nilness/nilness.go
1// Copyright 2018 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 nilness inspects the control-flow graph of an SSA function
6// and reports errors such as nil pointer dereferences and degenerate
7// nil pointer comparisons.
8package nilness
9
10import (
11    "fmt"
12    "go/token"
13    "go/types"
14
15    "golang.org/x/tools/go/analysis"
16    "golang.org/x/tools/go/analysis/passes/buildssa"
17    "golang.org/x/tools/go/ssa"
18)
19
20const Doc = `check for redundant or impossible nil comparisons
21
22The nilness checker inspects the control-flow graph of each function in
23a package and reports nil pointer dereferences, degenerate nil
24pointers, and panics with nil values. A degenerate comparison is of the form
25x==nil or x!=nil where x is statically known to be nil or non-nil. These are
26often a mistake, especially in control flow related to errors. Panics with nil
27values are checked because they are not detectable by
28
29    if r := recover(); r != nil {
30
31This check reports conditions such as:
32
33    if f == nil { // impossible condition (f is a function)
34    }
35
36and:
37
38    p := &v
39    ...
40    if p != nil { // tautological condition
41    }
42
43and:
44
45    if p == nil {
46        print(*p) // nil dereference
47    }
48
49and:
50
51    if p == nil {
52        panic(p)
53    }
54`
55
56var Analyzer = &analysis.Analyzer{
57    Name:     "nilness",
58    Doc:      Doc,
59    Run:      run,
60    Requires: []*analysis.Analyzer{buildssa.Analyzer},
61}
62
63func run(pass *analysis.Pass) (interface{}, error) {
64    ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
65    for _fn := range ssainput.SrcFuncs {
66        runFunc(passfn)
67    }
68    return nilnil
69}
70
71func runFunc(pass *analysis.Passfn *ssa.Function) {
72    reportf := func(category stringpos token.Posformat stringargs ...interface{}) {
73        pass.Report(analysis.Diagnostic{
74            Pos:      pos,
75            Categorycategory,
76            Message:  fmt.Sprintf(formatargs...),
77        })
78    }
79
80    // notNil reports an error if v is provably nil.
81    notNil := func(stack []factinstr ssa.Instructionv ssa.Valuedescr string) {
82        if nilnessOf(stackv) == isnil {
83            reportf("nilderef"instr.Pos(), "nil dereference in "+descr)
84        }
85    }
86
87    // visit visits reachable blocks of the CFG in dominance order,
88    // maintaining a stack of dominating nilness facts.
89    //
90    // By traversing the dom tree, we can pop facts off the stack as
91    // soon as we've visited a subtree.  Had we traversed the CFG,
92    // we would need to retain the set of facts for each block.
93    seen := make([]boollen(fn.Blocks)) // seen[i] means visit should ignore block i
94    var visit func(b *ssa.BasicBlockstack []fact)
95    visit = func(b *ssa.BasicBlockstack []fact) {
96        if seen[b.Index] {
97            return
98        }
99        seen[b.Index] = true
100
101        // Report nil dereferences.
102        for _instr := range b.Instrs {
103            switch instr := instr.(type) {
104            case ssa.CallInstruction:
105                notNil(stackinstrinstr.Common().Value,
106                    instr.Common().Description())
107            case *ssa.FieldAddr:
108                notNil(stackinstrinstr.X"field selection")
109            case *ssa.IndexAddr:
110                notNil(stackinstrinstr.X"index operation")
111            case *ssa.MapUpdate:
112                notNil(stackinstrinstr.Map"map update")
113            case *ssa.Slice:
114                // A nilcheck occurs in ptr[:] iff ptr is a pointer to an array.
115                if _ok := instr.X.Type().Underlying().(*types.Pointer); ok {
116                    notNil(stackinstrinstr.X"slice operation")
117                }
118            case *ssa.Store:
119                notNil(stackinstrinstr.Addr"store")
120            case *ssa.TypeAssert:
121                if !instr.CommaOk {
122                    notNil(stackinstrinstr.X"type assertion")
123                }
124            case *ssa.UnOp:
125                if instr.Op == token.MUL { // *X
126                    notNil(stackinstrinstr.X"load")
127                }
128            }
129        }
130
131        // Look for panics with nil value
132        for _instr := range b.Instrs {
133            switch instr := instr.(type) {
134            case *ssa.Panic:
135                if nilnessOf(stackinstr.X) == isnil {
136                    reportf("nilpanic"instr.Pos(), "panic with nil value")
137                }
138            case *ssa.SliceToArrayPointer:
139                nn := nilnessOf(stackinstr.X)
140                if nn == isnil && slice2ArrayPtrLen(instr) > 0 {
141                    reportf("conversionpanic"instr.Pos(), "nil slice being cast to an array of len > 0 will always panic")
142                }
143            }
144        }
145
146        // For nil comparison blocks, report an error if the condition
147        // is degenerate, and push a nilness fact on the stack when
148        // visiting its true and false successor blocks.
149        if binoptsuccfsucc := eq(b); binop != nil {
150            xnil := nilnessOf(stackbinop.X)
151            ynil := nilnessOf(stackbinop.Y)
152
153            if ynil != unknown && xnil != unknown && (xnil == isnil || ynil == isnil) {
154                // Degenerate condition:
155                // the nilness of both operands is known,
156                // and at least one of them is nil.
157                var adj string
158                if (xnil == ynil) == (binop.Op == token.EQL) {
159                    adj = "tautological"
160                } else {
161                    adj = "impossible"
162                }
163                reportf("cond"binop.Pos(), "%s condition: %s %s %s"adjxnilbinop.Opynil)
164
165                // If tsucc's or fsucc's sole incoming edge is impossible,
166                // it is unreachable.  Prune traversal of it and
167                // all the blocks it dominates.
168                // (We could be more precise with full dataflow
169                // analysis of control-flow joins.)
170                var skip *ssa.BasicBlock
171                if xnil == ynil {
172                    skip = fsucc
173                } else {
174                    skip = tsucc
175                }
176                for _d := range b.Dominees() {
177                    if d == skip && len(d.Preds) == 1 {
178                        continue
179                    }
180                    visit(dstack)
181                }
182                return
183            }
184
185            // "if x == nil" or "if nil == y" condition; x, y are unknown.
186            if xnil == isnil || ynil == isnil {
187                var newFacts facts
188                if xnil == isnil {
189                    // x is nil, y is unknown:
190                    // t successor learns y is nil.
191                    newFacts = expandFacts(fact{binop.Yisnil})
192                } else {
193                    // x is nil, y is unknown:
194                    // t successor learns x is nil.
195                    newFacts = expandFacts(fact{binop.Xisnil})
196                }
197
198                for _d := range b.Dominees() {
199                    // Successor blocks learn a fact
200                    // only at non-critical edges.
201                    // (We could do be more precise with full dataflow
202                    // analysis of control-flow joins.)
203                    s := stack
204                    if len(d.Preds) == 1 {
205                        if d == tsucc {
206                            s = append(snewFacts...)
207                        } else if d == fsucc {
208                            s = append(snewFacts.negate()...)
209                        }
210                    }
211                    visit(ds)
212                }
213                return
214            }
215        }
216
217        for _d := range b.Dominees() {
218            visit(dstack)
219        }
220    }
221
222    // Visit the entry block.  No need to visit fn.Recover.
223    if fn.Blocks != nil {
224        visit(fn.Blocks[0], make([]fact020)) // 20 is plenty
225    }
226}
227
228// A fact records that a block is dominated
229// by the condition v == nil or v != nil.
230type fact struct {
231    value   ssa.Value
232    nilness nilness
233}
234
235func (f factnegate() fact { return fact{f.value, -f.nilness} }
236
237type nilness int
238
239const (
240    isnonnil         = -1
241    unknown  nilness = 0
242    isnil            = 1
243)
244
245var nilnessStrings = []string{"non-nil""unknown""nil"}
246
247func (n nilnessString() string { return nilnessStrings[n+1] }
248
249// nilnessOf reports whether v is definitely nil, definitely not nil,
250// or unknown given the dominating stack of facts.
251func nilnessOf(stack []factv ssa.Valuenilness {
252    switch v := v.(type) {
253    // unwrap ChangeInterface and Slice values recursively, to detect if underlying
254    // values have any facts recorded or are otherwise known with regard to nilness.
255    //
256    // This work must be in addition to expanding facts about
257    // ChangeInterfaces during inference/fact gathering because this covers
258    // cases where the nilness of a value is intrinsic, rather than based
259    // on inferred facts, such as a zero value interface variable. That
260    // said, this work alone would only inform us when facts are about
261    // underlying values, rather than outer values, when the analysis is
262    // transitive in both directions.
263    case *ssa.ChangeInterface:
264        if underlying := nilnessOf(stackv.X); underlying != unknown {
265            return underlying
266        }
267    case *ssa.Slice:
268        if underlying := nilnessOf(stackv.X); underlying != unknown {
269            return underlying
270        }
271    case *ssa.SliceToArrayPointer:
272        nn := nilnessOf(stackv.X)
273        if slice2ArrayPtrLen(v) > 0 {
274            if nn == isnil {
275                // We know that *(*[1]byte)(nil) is going to panic because of the
276                // conversion. So return unknown to the caller, prevent useless
277                // nil deference reporting due to * operator.
278                return unknown
279            }
280            // Otherwise, the conversion will yield a non-nil pointer to array.
281            // Note that the instruction can still panic if array length greater
282            // than slice length. If the value is used by another instruction,
283            // that instruction can assume the panic did not happen when that
284            // instruction is reached.
285            return isnonnil
286        }
287        // In case array length is zero, the conversion result depends on nilness of the slice.
288        if nn != unknown {
289            return nn
290        }
291    }
292
293    // Is value intrinsically nil or non-nil?
294    switch v := v.(type) {
295    case *ssa.Alloc,
296        *ssa.FieldAddr,
297        *ssa.FreeVar,
298        *ssa.Function,
299        *ssa.Global,
300        *ssa.IndexAddr,
301        *ssa.MakeChan,
302        *ssa.MakeClosure,
303        *ssa.MakeInterface,
304        *ssa.MakeMap,
305        *ssa.MakeSlice:
306        return isnonnil
307    case *ssa.Const:
308        if v.IsNil() {
309            return isnil // nil or zero value of a pointer-like type
310        } else {
311            return unknown // non-pointer
312        }
313    }
314
315    // Search dominating control-flow facts.
316    for _f := range stack {
317        if f.value == v {
318            return f.nilness
319        }
320    }
321    return unknown
322}
323
324func slice2ArrayPtrLen(v *ssa.SliceToArrayPointerint64 {
325    return v.Type().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
326}
327
328// If b ends with an equality comparison, eq returns the operation and
329// its true (equal) and false (not equal) successors.
330func eq(b *ssa.BasicBlock) (op *ssa.BinOptsuccfsucc *ssa.BasicBlock) {
331    if Ifok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok {
332        if binopok := If.Cond.(*ssa.BinOp); ok {
333            switch binop.Op {
334            case token.EQL:
335                return binopb.Succs[0], b.Succs[1]
336            case token.NEQ:
337                return binopb.Succs[1], b.Succs[0]
338            }
339        }
340    }
341    return nilnilnil
342}
343
344// expandFacts takes a single fact and returns the set of facts that can be
345// known about it or any of its related values. Some operations, like
346// ChangeInterface, have transitive nilness, such that if you know the
347// underlying value is nil, you also know the value itself is nil, and vice
348// versa. This operation allows callers to match on any of the related values
349// in analyses, rather than just the one form of the value that happened to
350// appear in a comparison.
351//
352// This work must be in addition to unwrapping values within nilnessOf because
353// while this work helps give facts about transitively known values based on
354// inferred facts, the recursive check within nilnessOf covers cases where
355// nilness facts are intrinsic to the underlying value, such as a zero value
356// interface variables.
357//
358// ChangeInterface is the only expansion currently supported, but others, like
359// Slice, could be added. At this time, this tool does not check slice
360// operations in a way this expansion could help. See
361// https://play.golang.org/p/mGqXEp7w4fR for an example.
362func expandFacts(f fact) []fact {
363    ff := []fact{f}
364
365Loop:
366    for {
367        switch v := f.value.(type) {
368        case *ssa.ChangeInterface:
369            f = fact{v.Xf.nilness}
370            ff = append(fff)
371        default:
372            break Loop
373        }
374    }
375
376    return ff
377}
378
379type facts []fact
380
381func (ff factsnegate() facts {
382    nn := make([]factlen(ff))
383    for if := range ff {
384        nn[i] = f.negate()
385    }
386    return nn
387}
388
MembersX
runFunc.BlockStmt.tsucc
nilness
facts.negate.nn
run
nilnessOf.BlockStmt.nn
eq.op
expandFacts.f
types
run.RangeStmt_1542.fn
runFunc.fn
runFunc.BlockStmt.binop
nilnessOf.stack
eq
eq.b
expandFacts.ff
runFunc
runFunc.BlockStmt.fsucc
runFunc.BlockStmt.BlockStmt.BlockStmt.RangeStmt_5193.d
runFunc.BlockStmt.RangeStmt_6183.d
fact.value
isnil
nilnessOf.BlockStmt.underlying
run.pass
runFunc.BlockStmt.RangeStmt_3668.instr
nilness.String.n
nilnessOf.v
facts.negate
slice2ArrayPtrLen
facts.negate.ff
runFunc.BlockStmt.BlockStmt.ynil
runFunc.BlockStmt.BlockStmt.BlockStmt.newFacts
fact
fact.negate
nilnessOf
fmt
buildssa
runFunc.BlockStmt.RangeStmt_3668.BlockStmt.BlockStmt.nn
unknown
eq.tsucc
runFunc.BlockStmt.RangeStmt_2719.instr
runFunc.seen
runFunc.BlockStmt.BlockStmt.BlockStmt.RangeStmt_5746.BlockStmt.s
runFunc.BlockStmt.BlockStmt.BlockStmt.skip
fact.negate.f
analysis
ssa
Doc
runFunc.pass
runFunc.BlockStmt.BlockStmt.BlockStmt.adj
facts.negate.RangeStmt_11204.f
token
runFunc.BlockStmt.BlockStmt.xnil
nilnessOf.RangeStmt_9119.f
slice2ArrayPtrLen.v
eq.fsucc
facts.negate.RangeStmt_11204.i
runFunc.BlockStmt.BlockStmt.BlockStmt.RangeStmt_5746.d
fact.nilness
nilness.String
expandFacts
facts
Members
X