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.
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.
15 "golang.org/x/tools/go/analysis"
16 "golang.org/x/tools/go/analysis/passes/buildssa"
17 "golang.org/x/tools/go/ssa"
20 const Doc = `check for redundant or impossible nil comparisons
22 The nilness checker inspects the control-flow graph of each function in
23 a package and reports nil pointer dereferences, degenerate nil
24 pointers, and panics with nil values. A degenerate comparison is of the form
25 x==nil or x!=nil where x is statically known to be nil or non-nil. These are
26 often a mistake, especially in control flow related to errors. Panics with nil
27 values are checked because they are not detectable by
29 if r := recover(); r != nil {
31 This check reports conditions such as:
33 if f == nil { // impossible condition (f is a function)
40 if p != nil { // tautological condition
46 print(*p) // nil dereference
56 var Analyzer = &analysis.Analyzer{
60 Requires: []*analysis.Analyzer{buildssa.Analyzer},
63 func run(pass *analysis.Pass) (interface{}, error) {
64 ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
65 for _, fn := range ssainput.SrcFuncs {
71 func runFunc(pass *analysis.Pass, fn *ssa.Function) {
72 reportf := func(category string, pos token.Pos, format string, args ...interface{}) {
73 pass.Report(analysis.Diagnostic{
76 Message: fmt.Sprintf(format, args...),
80 // notNil reports an error if v is provably nil.
81 notNil := func(stack []fact, instr ssa.Instruction, v ssa.Value, descr string) {
82 if nilnessOf(stack, v) == isnil {
83 reportf("nilderef", instr.Pos(), "nil dereference in "+descr)
87 // visit visits reachable blocks of the CFG in dominance order,
88 // maintaining a stack of dominating nilness facts.
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([]bool, len(fn.Blocks)) // seen[i] means visit should ignore block i
94 var visit func(b *ssa.BasicBlock, stack []fact)
95 visit = func(b *ssa.BasicBlock, stack []fact) {
101 // Report nil dereferences.
102 for _, instr := range b.Instrs {
103 switch instr := instr.(type) {
104 case ssa.CallInstruction:
105 notNil(stack, instr, instr.Common().Value,
106 instr.Common().Description())
108 notNil(stack, instr, instr.X, "field selection")
110 notNil(stack, instr, instr.X, "index operation")
112 notNil(stack, instr, instr.Map, "map update")
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(stack, instr, instr.X, "slice operation")
119 notNil(stack, instr, instr.Addr, "store")
120 case *ssa.TypeAssert:
122 notNil(stack, instr, instr.X, "type assertion")
125 if instr.Op == token.MUL { // *X
126 notNil(stack, instr, instr.X, "load")
131 // Look for panics with nil value
132 for _, instr := range b.Instrs {
133 switch instr := instr.(type) {
135 if nilnessOf(stack, instr.X) == isnil {
136 reportf("nilpanic", instr.Pos(), "panic with nil value")
141 // For nil comparison blocks, report an error if the condition
142 // is degenerate, and push a nilness fact on the stack when
143 // visiting its true and false successor blocks.
144 if binop, tsucc, fsucc := eq(b); binop != nil {
145 xnil := nilnessOf(stack, binop.X)
146 ynil := nilnessOf(stack, binop.Y)
148 if ynil != unknown && xnil != unknown && (xnil == isnil || ynil == isnil) {
149 // Degenerate condition:
150 // the nilness of both operands is known,
151 // and at least one of them is nil.
153 if (xnil == ynil) == (binop.Op == token.EQL) {
158 reportf("cond", binop.Pos(), "%s condition: %s %s %s", adj, xnil, binop.Op, ynil)
160 // If tsucc's or fsucc's sole incoming edge is impossible,
161 // it is unreachable. Prune traversal of it and
162 // all the blocks it dominates.
163 // (We could be more precise with full dataflow
164 // analysis of control-flow joins.)
165 var skip *ssa.BasicBlock
171 for _, d := range b.Dominees() {
172 if d == skip && len(d.Preds) == 1 {
180 // "if x == nil" or "if nil == y" condition; x, y are unknown.
181 if xnil == isnil || ynil == isnil {
184 // x is nil, y is unknown:
185 // t successor learns y is nil.
186 newFacts = expandFacts(fact{binop.Y, isnil})
188 // x is nil, y is unknown:
189 // t successor learns x is nil.
190 newFacts = expandFacts(fact{binop.X, isnil})
193 for _, d := range b.Dominees() {
194 // Successor blocks learn a fact
195 // only at non-critical edges.
196 // (We could do be more precise with full dataflow
197 // analysis of control-flow joins.)
199 if len(d.Preds) == 1 {
201 s = append(s, newFacts...)
202 } else if d == fsucc {
203 s = append(s, newFacts.negate()...)
212 for _, d := range b.Dominees() {
217 // Visit the entry block. No need to visit fn.Recover.
218 if fn.Blocks != nil {
219 visit(fn.Blocks[0], make([]fact, 0, 20)) // 20 is plenty
223 // A fact records that a block is dominated
224 // by the condition v == nil or v != nil.
230 func (f fact) negate() fact { return fact{f.value, -f.nilness} }
240 var nilnessStrings = []string{"non-nil", "unknown", "nil"}
242 func (n nilness) String() string { return nilnessStrings[n+1] }
244 // nilnessOf reports whether v is definitely nil, definitely not nil,
245 // or unknown given the dominating stack of facts.
246 func nilnessOf(stack []fact, v ssa.Value) nilness {
247 switch v := v.(type) {
248 // unwrap ChangeInterface values recursively, to detect if underlying
249 // values have any facts recorded or are otherwise known with regard to nilness.
251 // This work must be in addition to expanding facts about
252 // ChangeInterfaces during inference/fact gathering because this covers
253 // cases where the nilness of a value is intrinsic, rather than based
254 // on inferred facts, such as a zero value interface variable. That
255 // said, this work alone would only inform us when facts are about
256 // underlying values, rather than outer values, when the analysis is
257 // transitive in both directions.
258 case *ssa.ChangeInterface:
259 if underlying := nilnessOf(stack, v.X); underlying != unknown {
264 // Is value intrinsically nil or non-nil?
265 switch v := v.(type) {
286 // Search dominating control-flow facts.
287 for _, f := range stack {
295 // If b ends with an equality comparison, eq returns the operation and
296 // its true (equal) and false (not equal) successors.
297 func eq(b *ssa.BasicBlock) (op *ssa.BinOp, tsucc, fsucc *ssa.BasicBlock) {
298 if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok {
299 if binop, ok := If.Cond.(*ssa.BinOp); ok {
302 return binop, b.Succs[0], b.Succs[1]
304 return binop, b.Succs[1], b.Succs[0]
311 // expandFacts takes a single fact and returns the set of facts that can be
312 // known about it or any of its related values. Some operations, like
313 // ChangeInterface, have transitive nilness, such that if you know the
314 // underlying value is nil, you also know the value itself is nil, and vice
315 // versa. This operation allows callers to match on any of the related values
316 // in analyses, rather than just the one form of the value that happend to
317 // appear in a comparison.
319 // This work must be in addition to unwrapping values within nilnessOf because
320 // while this work helps give facts about transitively known values based on
321 // inferred facts, the recursive check within nilnessOf covers cases where
322 // nilness facts are intrinsic to the underlying value, such as a zero value
323 // interface variables.
325 // ChangeInterface is the only expansion currently supported, but others, like
326 // Slice, could be added. At this time, this tool does not check slice
327 // operations in a way this expansion could help. See
328 // https://play.golang.org/p/mGqXEp7w4fR for an example.
329 func expandFacts(f fact) []fact {
334 switch v := f.value.(type) {
335 case *ssa.ChangeInterface:
336 f = fact{v.X, f.nilness}
348 func (ff facts) negate() facts {
349 nn := make([]fact, len(ff))
350 for i, f := range ff {