github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/go/analysis/passes/nilness/nilness.go (about)

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