github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/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  	"github.com/powerman/golang-tools/go/analysis"
    16  	"github.com/powerman/golang-tools/go/analysis/passes/buildssa"
    17  	"github.com/powerman/golang-tools/go/ssa"
    18  )
    19  
    20  const Doc = `check for redundant or impossible nil comparisons
    21  
    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
    28  
    29  	if r := recover(); r != nil {
    30  
    31  This check reports conditions such as:
    32  
    33  	if f == nil { // impossible condition (f is a function)
    34  	}
    35  
    36  and:
    37  
    38  	p := &v
    39  	...
    40  	if p != nil { // tautological condition
    41  	}
    42  
    43  and:
    44  
    45  	if p == nil {
    46  		print(*p) // nil dereference
    47  	}
    48  
    49  and:
    50  
    51  	if p == nil {
    52  		panic(p)
    53  	}
    54  `
    55  
    56  var Analyzer = &analysis.Analyzer{
    57  	Name:     "nilness",
    58  	Doc:      Doc,
    59  	Run:      run,
    60  	Requires: []*analysis.Analyzer{buildssa.Analyzer},
    61  }
    62  
    63  func run(pass *analysis.Pass) (interface{}, error) {
    64  	ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
    65  	for _, fn := range ssainput.SrcFuncs {
    66  		runFunc(pass, fn)
    67  	}
    68  	return nil, nil
    69  }
    70  
    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{
    74  			Pos:      pos,
    75  			Category: category,
    76  			Message:  fmt.Sprintf(format, args...),
    77  		})
    78  	}
    79  
    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)
    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([]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) {
    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(stack, instr, instr.Common().Value,
   106  					instr.Common().Description())
   107  			case *ssa.FieldAddr:
   108  				notNil(stack, instr, instr.X, "field selection")
   109  			case *ssa.IndexAddr:
   110  				notNil(stack, instr, instr.X, "index operation")
   111  			case *ssa.MapUpdate:
   112  				notNil(stack, instr, instr.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(stack, instr, instr.X, "slice operation")
   117  				}
   118  			case *ssa.Store:
   119  				notNil(stack, instr, instr.Addr, "store")
   120  			case *ssa.TypeAssert:
   121  				if !instr.CommaOk {
   122  					notNil(stack, instr, instr.X, "type assertion")
   123  				}
   124  			case *ssa.UnOp:
   125  				if instr.Op == token.MUL { // *X
   126  					notNil(stack, instr, instr.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(stack, instr.X) == isnil {
   136  					reportf("nilpanic", instr.Pos(), "panic with nil value")
   137  				}
   138  			case *ssa.SliceToArrayPointer:
   139  				nn := nilnessOf(stack, instr.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 binop, tsucc, fsucc := eq(b); binop != nil {
   150  			xnil := nilnessOf(stack, binop.X)
   151  			ynil := nilnessOf(stack, binop.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", adj, xnil, binop.Op, ynil)
   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(d, stack)
   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.Y, isnil})
   192  				} else {
   193  					// x is nil, y is unknown:
   194  					// t successor learns x is nil.
   195  					newFacts = expandFacts(fact{binop.X, isnil})
   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(s, newFacts...)
   207  						} else if d == fsucc {
   208  							s = append(s, newFacts.negate()...)
   209  						}
   210  					}
   211  					visit(d, s)
   212  				}
   213  				return
   214  			}
   215  		}
   216  
   217  		for _, d := range b.Dominees() {
   218  			visit(d, stack)
   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([]fact, 0, 20)) // 20 is plenty
   225  	}
   226  }
   227  
   228  // A fact records that a block is dominated
   229  // by the condition v == nil or v != nil.
   230  type fact struct {
   231  	value   ssa.Value
   232  	nilness nilness
   233  }
   234  
   235  func (f fact) negate() fact { return fact{f.value, -f.nilness} }
   236  
   237  type nilness int
   238  
   239  const (
   240  	isnonnil         = -1
   241  	unknown  nilness = 0
   242  	isnil            = 1
   243  )
   244  
   245  var nilnessStrings = []string{"non-nil", "unknown", "nil"}
   246  
   247  func (n nilness) String() 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.
   251  func nilnessOf(stack []fact, v ssa.Value) nilness {
   252  	switch v := v.(type) {
   253  	// unwrap ChangeInterface 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(stack, v.X); underlying != unknown {
   265  			return underlying
   266  		}
   267  	case *ssa.SliceToArrayPointer:
   268  		nn := nilnessOf(stack, v.X)
   269  		if slice2ArrayPtrLen(v) > 0 {
   270  			if nn == isnil {
   271  				// We know that *(*[1]byte)(nil) is going to panic because of the
   272  				// conversion. So return unknown to the caller, prevent useless
   273  				// nil deference reporting due to * operator.
   274  				return unknown
   275  			}
   276  			// Otherwise, the conversion will yield a non-nil pointer to array.
   277  			// Note that the instruction can still panic if array length greater
   278  			// than slice length. If the value is used by another instruction,
   279  			// that instruction can assume the panic did not happen when that
   280  			// instruction is reached.
   281  			return isnonnil
   282  		}
   283  		// In case array length is zero, the conversion result depends on nilness of the slice.
   284  		if nn != unknown {
   285  			return nn
   286  		}
   287  	}
   288  
   289  	// Is value intrinsically nil or non-nil?
   290  	switch v := v.(type) {
   291  	case *ssa.Alloc,
   292  		*ssa.FieldAddr,
   293  		*ssa.FreeVar,
   294  		*ssa.Function,
   295  		*ssa.Global,
   296  		*ssa.IndexAddr,
   297  		*ssa.MakeChan,
   298  		*ssa.MakeClosure,
   299  		*ssa.MakeInterface,
   300  		*ssa.MakeMap,
   301  		*ssa.MakeSlice:
   302  		return isnonnil
   303  	case *ssa.Const:
   304  		if v.IsNil() {
   305  			return isnil
   306  		} else {
   307  			return isnonnil
   308  		}
   309  	}
   310  
   311  	// Search dominating control-flow facts.
   312  	for _, f := range stack {
   313  		if f.value == v {
   314  			return f.nilness
   315  		}
   316  	}
   317  	return unknown
   318  }
   319  
   320  func slice2ArrayPtrLen(v *ssa.SliceToArrayPointer) int64 {
   321  	return v.Type().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
   322  }
   323  
   324  // If b ends with an equality comparison, eq returns the operation and
   325  // its true (equal) and false (not equal) successors.
   326  func eq(b *ssa.BasicBlock) (op *ssa.BinOp, tsucc, fsucc *ssa.BasicBlock) {
   327  	if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok {
   328  		if binop, ok := If.Cond.(*ssa.BinOp); ok {
   329  			switch binop.Op {
   330  			case token.EQL:
   331  				return binop, b.Succs[0], b.Succs[1]
   332  			case token.NEQ:
   333  				return binop, b.Succs[1], b.Succs[0]
   334  			}
   335  		}
   336  	}
   337  	return nil, nil, nil
   338  }
   339  
   340  // expandFacts takes a single fact and returns the set of facts that can be
   341  // known about it or any of its related values. Some operations, like
   342  // ChangeInterface, have transitive nilness, such that if you know the
   343  // underlying value is nil, you also know the value itself is nil, and vice
   344  // versa. This operation allows callers to match on any of the related values
   345  // in analyses, rather than just the one form of the value that happened to
   346  // appear in a comparison.
   347  //
   348  // This work must be in addition to unwrapping values within nilnessOf because
   349  // while this work helps give facts about transitively known values based on
   350  // inferred facts, the recursive check within nilnessOf covers cases where
   351  // nilness facts are intrinsic to the underlying value, such as a zero value
   352  // interface variables.
   353  //
   354  // ChangeInterface is the only expansion currently supported, but others, like
   355  // Slice, could be added. At this time, this tool does not check slice
   356  // operations in a way this expansion could help. See
   357  // https://play.golang.org/p/mGqXEp7w4fR for an example.
   358  func expandFacts(f fact) []fact {
   359  	ff := []fact{f}
   360  
   361  Loop:
   362  	for {
   363  		switch v := f.value.(type) {
   364  		case *ssa.ChangeInterface:
   365  			f = fact{v.X, f.nilness}
   366  			ff = append(ff, f)
   367  		default:
   368  			break Loop
   369  		}
   370  	}
   371  
   372  	return ff
   373  }
   374  
   375  type facts []fact
   376  
   377  func (ff facts) negate() facts {
   378  	nn := make([]fact, len(ff))
   379  	for i, f := range ff {
   380  		nn[i] = f.negate()
   381  	}
   382  	return nn
   383  }