...

Source file src/golang.org/x/tools/go/analysis/passes/nilness/nilness.go

Documentation: golang.org/x/tools/go/analysis/passes/nilness

     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
     6  
     7  import (
     8  	_ "embed"
     9  	"fmt"
    10  	"go/token"
    11  	"go/types"
    12  
    13  	"golang.org/x/tools/go/analysis"
    14  	"golang.org/x/tools/go/analysis/passes/buildssa"
    15  	"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
    16  	"golang.org/x/tools/go/ssa"
    17  	"golang.org/x/tools/internal/typeparams"
    18  )
    19  
    20  //go:embed doc.go
    21  var doc string
    22  
    23  var Analyzer = &analysis.Analyzer{
    24  	Name:     "nilness",
    25  	Doc:      analysisutil.MustExtractDoc(doc, "nilness"),
    26  	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/nilness",
    27  	Run:      run,
    28  	Requires: []*analysis.Analyzer{buildssa.Analyzer},
    29  }
    30  
    31  func run(pass *analysis.Pass) (interface{}, error) {
    32  	ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
    33  	for _, fn := range ssainput.SrcFuncs {
    34  		runFunc(pass, fn)
    35  	}
    36  	return nil, nil
    37  }
    38  
    39  func runFunc(pass *analysis.Pass, fn *ssa.Function) {
    40  	reportf := func(category string, pos token.Pos, format string, args ...interface{}) {
    41  		// We ignore nil-checking ssa.Instructions
    42  		// that don't correspond to syntax.
    43  		if pos.IsValid() {
    44  			pass.Report(analysis.Diagnostic{
    45  				Pos:      pos,
    46  				Category: category,
    47  				Message:  fmt.Sprintf(format, args...),
    48  			})
    49  		}
    50  	}
    51  
    52  	// notNil reports an error if v is provably nil.
    53  	notNil := func(stack []fact, instr ssa.Instruction, v ssa.Value, descr string) {
    54  		if nilnessOf(stack, v) == isnil {
    55  			reportf("nilderef", instr.Pos(), descr)
    56  		}
    57  	}
    58  
    59  	// visit visits reachable blocks of the CFG in dominance order,
    60  	// maintaining a stack of dominating nilness facts.
    61  	//
    62  	// By traversing the dom tree, we can pop facts off the stack as
    63  	// soon as we've visited a subtree.  Had we traversed the CFG,
    64  	// we would need to retain the set of facts for each block.
    65  	seen := make([]bool, len(fn.Blocks)) // seen[i] means visit should ignore block i
    66  	var visit func(b *ssa.BasicBlock, stack []fact)
    67  	visit = func(b *ssa.BasicBlock, stack []fact) {
    68  		if seen[b.Index] {
    69  			return
    70  		}
    71  		seen[b.Index] = true
    72  
    73  		// Report nil dereferences.
    74  		for _, instr := range b.Instrs {
    75  			switch instr := instr.(type) {
    76  			case ssa.CallInstruction:
    77  				// A nil receiver may be okay for type params.
    78  				cc := instr.Common()
    79  				if !(cc.IsInvoke() && typeparams.IsTypeParam(cc.Value.Type())) {
    80  					notNil(stack, instr, cc.Value, "nil dereference in "+cc.Description())
    81  				}
    82  			case *ssa.FieldAddr:
    83  				notNil(stack, instr, instr.X, "nil dereference in field selection")
    84  			case *ssa.IndexAddr:
    85  				switch typeparams.CoreType(instr.X.Type()).(type) {
    86  				case *types.Pointer: // *array
    87  					notNil(stack, instr, instr.X, "nil dereference in array index operation")
    88  				case *types.Slice:
    89  					// This is not necessarily a runtime error, because
    90  					// it is usually dominated by a bounds check.
    91  					if isRangeIndex(instr) {
    92  						notNil(stack, instr, instr.X, "range of nil slice")
    93  					} else {
    94  						notNil(stack, instr, instr.X, "index of nil slice")
    95  					}
    96  				}
    97  			case *ssa.MapUpdate:
    98  				notNil(stack, instr, instr.Map, "nil dereference in map update")
    99  			case *ssa.Range:
   100  				// (Not a runtime error, but a likely mistake.)
   101  				notNil(stack, instr, instr.X, "range over nil map")
   102  			case *ssa.Slice:
   103  				// A nilcheck occurs in ptr[:] iff ptr is a pointer to an array.
   104  				if is[*types.Pointer](instr.X.Type().Underlying()) {
   105  					notNil(stack, instr, instr.X, "nil dereference in slice operation")
   106  				}
   107  			case *ssa.Store:
   108  				notNil(stack, instr, instr.Addr, "nil dereference in store")
   109  			case *ssa.TypeAssert:
   110  				if !instr.CommaOk {
   111  					notNil(stack, instr, instr.X, "nil dereference in type assertion")
   112  				}
   113  			case *ssa.UnOp:
   114  				switch instr.Op {
   115  				case token.MUL: // *X
   116  					notNil(stack, instr, instr.X, "nil dereference in load")
   117  				case token.ARROW: // <-ch
   118  					// (Not a runtime error, but a likely mistake.)
   119  					notNil(stack, instr, instr.X, "receive from nil channel")
   120  				}
   121  			case *ssa.Send:
   122  				// (Not a runtime error, but a likely mistake.)
   123  				notNil(stack, instr, instr.Chan, "send to nil channel")
   124  			}
   125  		}
   126  
   127  		// Look for panics with nil value
   128  		for _, instr := range b.Instrs {
   129  			switch instr := instr.(type) {
   130  			case *ssa.Panic:
   131  				if nilnessOf(stack, instr.X) == isnil {
   132  					reportf("nilpanic", instr.Pos(), "panic with nil value")
   133  				}
   134  			case *ssa.SliceToArrayPointer:
   135  				nn := nilnessOf(stack, instr.X)
   136  				if nn == isnil && slice2ArrayPtrLen(instr) > 0 {
   137  					reportf("conversionpanic", instr.Pos(), "nil slice being cast to an array of len > 0 will always panic")
   138  				}
   139  			}
   140  		}
   141  
   142  		// For nil comparison blocks, report an error if the condition
   143  		// is degenerate, and push a nilness fact on the stack when
   144  		// visiting its true and false successor blocks.
   145  		if binop, tsucc, fsucc := eq(b); binop != nil {
   146  			xnil := nilnessOf(stack, binop.X)
   147  			ynil := nilnessOf(stack, binop.Y)
   148  
   149  			if ynil != unknown && xnil != unknown && (xnil == isnil || ynil == isnil) {
   150  				// Degenerate condition:
   151  				// the nilness of both operands is known,
   152  				// and at least one of them is nil.
   153  				var adj string
   154  				if (xnil == ynil) == (binop.Op == token.EQL) {
   155  					adj = "tautological"
   156  				} else {
   157  					adj = "impossible"
   158  				}
   159  				reportf("cond", binop.Pos(), "%s condition: %s %s %s", adj, xnil, binop.Op, ynil)
   160  
   161  				// If tsucc's or fsucc's sole incoming edge is impossible,
   162  				// it is unreachable.  Prune traversal of it and
   163  				// all the blocks it dominates.
   164  				// (We could be more precise with full dataflow
   165  				// analysis of control-flow joins.)
   166  				var skip *ssa.BasicBlock
   167  				if xnil == ynil {
   168  					skip = fsucc
   169  				} else {
   170  					skip = tsucc
   171  				}
   172  				for _, d := range b.Dominees() {
   173  					if d == skip && len(d.Preds) == 1 {
   174  						continue
   175  					}
   176  					visit(d, stack)
   177  				}
   178  				return
   179  			}
   180  
   181  			// "if x == nil" or "if nil == y" condition; x, y are unknown.
   182  			if xnil == isnil || ynil == isnil {
   183  				var newFacts facts
   184  				if xnil == isnil {
   185  					// x is nil, y is unknown:
   186  					// t successor learns y is nil.
   187  					newFacts = expandFacts(fact{binop.Y, isnil})
   188  				} else {
   189  					// x is nil, y is unknown:
   190  					// t successor learns x is nil.
   191  					newFacts = expandFacts(fact{binop.X, isnil})
   192  				}
   193  
   194  				for _, d := range b.Dominees() {
   195  					// Successor blocks learn a fact
   196  					// only at non-critical edges.
   197  					// (We could do be more precise with full dataflow
   198  					// analysis of control-flow joins.)
   199  					s := stack
   200  					if len(d.Preds) == 1 {
   201  						if d == tsucc {
   202  							s = append(s, newFacts...)
   203  						} else if d == fsucc {
   204  							s = append(s, newFacts.negate()...)
   205  						}
   206  					}
   207  					visit(d, s)
   208  				}
   209  				return
   210  			}
   211  		}
   212  
   213  		// In code of the form:
   214  		//
   215  		// 	if ptr, ok := x.(*T); ok { ... } else { fsucc }
   216  		//
   217  		// the fsucc block learns that ptr == nil,
   218  		// since that's its zero value.
   219  		if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok {
   220  			// Handle "if ok" and "if !ok" variants.
   221  			cond, fsucc := If.Cond, b.Succs[1]
   222  			if unop, ok := cond.(*ssa.UnOp); ok && unop.Op == token.NOT {
   223  				cond, fsucc = unop.X, b.Succs[0]
   224  			}
   225  
   226  			// Match pattern:
   227  			//   t0 = typeassert (pointerlike)
   228  			//   t1 = extract t0 #0  // ptr
   229  			//   t2 = extract t0 #1  // ok
   230  			//   if t2 goto tsucc, fsucc
   231  			if extract1, ok := cond.(*ssa.Extract); ok && extract1.Index == 1 {
   232  				if assert, ok := extract1.Tuple.(*ssa.TypeAssert); ok &&
   233  					isNillable(assert.AssertedType) {
   234  					for _, pinstr := range *assert.Referrers() {
   235  						if extract0, ok := pinstr.(*ssa.Extract); ok &&
   236  							extract0.Index == 0 &&
   237  							extract0.Tuple == extract1.Tuple {
   238  							for _, d := range b.Dominees() {
   239  								if len(d.Preds) == 1 && d == fsucc {
   240  									visit(d, append(stack, fact{extract0, isnil}))
   241  								}
   242  							}
   243  						}
   244  					}
   245  				}
   246  			}
   247  		}
   248  
   249  		for _, d := range b.Dominees() {
   250  			visit(d, stack)
   251  		}
   252  	}
   253  
   254  	// Visit the entry block.  No need to visit fn.Recover.
   255  	if fn.Blocks != nil {
   256  		visit(fn.Blocks[0], make([]fact, 0, 20)) // 20 is plenty
   257  	}
   258  }
   259  
   260  // A fact records that a block is dominated
   261  // by the condition v == nil or v != nil.
   262  type fact struct {
   263  	value   ssa.Value
   264  	nilness nilness
   265  }
   266  
   267  func (f fact) negate() fact { return fact{f.value, -f.nilness} }
   268  
   269  type nilness int
   270  
   271  const (
   272  	isnonnil         = -1
   273  	unknown  nilness = 0
   274  	isnil            = 1
   275  )
   276  
   277  var nilnessStrings = []string{"non-nil", "unknown", "nil"}
   278  
   279  func (n nilness) String() string { return nilnessStrings[n+1] }
   280  
   281  // nilnessOf reports whether v is definitely nil, definitely not nil,
   282  // or unknown given the dominating stack of facts.
   283  func nilnessOf(stack []fact, v ssa.Value) nilness {
   284  	switch v := v.(type) {
   285  	// unwrap ChangeInterface and Slice values recursively, to detect if underlying
   286  	// values have any facts recorded or are otherwise known with regard to nilness.
   287  	//
   288  	// This work must be in addition to expanding facts about
   289  	// ChangeInterfaces during inference/fact gathering because this covers
   290  	// cases where the nilness of a value is intrinsic, rather than based
   291  	// on inferred facts, such as a zero value interface variable. That
   292  	// said, this work alone would only inform us when facts are about
   293  	// underlying values, rather than outer values, when the analysis is
   294  	// transitive in both directions.
   295  	case *ssa.ChangeInterface:
   296  		if underlying := nilnessOf(stack, v.X); underlying != unknown {
   297  			return underlying
   298  		}
   299  	case *ssa.Slice:
   300  		if underlying := nilnessOf(stack, v.X); underlying != unknown {
   301  			return underlying
   302  		}
   303  	case *ssa.SliceToArrayPointer:
   304  		nn := nilnessOf(stack, v.X)
   305  		if slice2ArrayPtrLen(v) > 0 {
   306  			if nn == isnil {
   307  				// We know that *(*[1]byte)(nil) is going to panic because of the
   308  				// conversion. So return unknown to the caller, prevent useless
   309  				// nil deference reporting due to * operator.
   310  				return unknown
   311  			}
   312  			// Otherwise, the conversion will yield a non-nil pointer to array.
   313  			// Note that the instruction can still panic if array length greater
   314  			// than slice length. If the value is used by another instruction,
   315  			// that instruction can assume the panic did not happen when that
   316  			// instruction is reached.
   317  			return isnonnil
   318  		}
   319  		// In case array length is zero, the conversion result depends on nilness of the slice.
   320  		if nn != unknown {
   321  			return nn
   322  		}
   323  	}
   324  
   325  	// Is value intrinsically nil or non-nil?
   326  	switch v := v.(type) {
   327  	case *ssa.Alloc,
   328  		*ssa.FieldAddr,
   329  		*ssa.FreeVar,
   330  		*ssa.Function,
   331  		*ssa.Global,
   332  		*ssa.IndexAddr,
   333  		*ssa.MakeChan,
   334  		*ssa.MakeClosure,
   335  		*ssa.MakeInterface,
   336  		*ssa.MakeMap,
   337  		*ssa.MakeSlice:
   338  		return isnonnil
   339  	case *ssa.Const:
   340  		if v.IsNil() {
   341  			return isnil // nil or zero value of a pointer-like type
   342  		} else {
   343  			return unknown // non-pointer
   344  		}
   345  	}
   346  
   347  	// Search dominating control-flow facts.
   348  	for _, f := range stack {
   349  		if f.value == v {
   350  			return f.nilness
   351  		}
   352  	}
   353  	return unknown
   354  }
   355  
   356  func slice2ArrayPtrLen(v *ssa.SliceToArrayPointer) int64 {
   357  	return v.Type().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
   358  }
   359  
   360  // If b ends with an equality comparison, eq returns the operation and
   361  // its true (equal) and false (not equal) successors.
   362  func eq(b *ssa.BasicBlock) (op *ssa.BinOp, tsucc, fsucc *ssa.BasicBlock) {
   363  	if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok {
   364  		if binop, ok := If.Cond.(*ssa.BinOp); ok {
   365  			switch binop.Op {
   366  			case token.EQL:
   367  				return binop, b.Succs[0], b.Succs[1]
   368  			case token.NEQ:
   369  				return binop, b.Succs[1], b.Succs[0]
   370  			}
   371  		}
   372  	}
   373  	return nil, nil, nil
   374  }
   375  
   376  // expandFacts takes a single fact and returns the set of facts that can be
   377  // known about it or any of its related values. Some operations, like
   378  // ChangeInterface, have transitive nilness, such that if you know the
   379  // underlying value is nil, you also know the value itself is nil, and vice
   380  // versa. This operation allows callers to match on any of the related values
   381  // in analyses, rather than just the one form of the value that happened to
   382  // appear in a comparison.
   383  //
   384  // This work must be in addition to unwrapping values within nilnessOf because
   385  // while this work helps give facts about transitively known values based on
   386  // inferred facts, the recursive check within nilnessOf covers cases where
   387  // nilness facts are intrinsic to the underlying value, such as a zero value
   388  // interface variables.
   389  //
   390  // ChangeInterface is the only expansion currently supported, but others, like
   391  // Slice, could be added. At this time, this tool does not check slice
   392  // operations in a way this expansion could help. See
   393  // https://play.golang.org/p/mGqXEp7w4fR for an example.
   394  func expandFacts(f fact) []fact {
   395  	ff := []fact{f}
   396  
   397  Loop:
   398  	for {
   399  		switch v := f.value.(type) {
   400  		case *ssa.ChangeInterface:
   401  			f = fact{v.X, f.nilness}
   402  			ff = append(ff, f)
   403  		default:
   404  			break Loop
   405  		}
   406  	}
   407  
   408  	return ff
   409  }
   410  
   411  type facts []fact
   412  
   413  func (ff facts) negate() facts {
   414  	nn := make([]fact, len(ff))
   415  	for i, f := range ff {
   416  		nn[i] = f.negate()
   417  	}
   418  	return nn
   419  }
   420  
   421  func is[T any](x any) bool {
   422  	_, ok := x.(T)
   423  	return ok
   424  }
   425  
   426  func isNillable(t types.Type) bool {
   427  	switch t := typeparams.CoreType(t).(type) {
   428  	case *types.Pointer,
   429  		*types.Map,
   430  		*types.Signature,
   431  		*types.Chan,
   432  		*types.Interface,
   433  		*types.Slice:
   434  		return true
   435  	case *types.Basic:
   436  		return t == types.Typ[types.UnsafePointer]
   437  	}
   438  	return false
   439  }
   440  
   441  // isRangeIndex reports whether the instruction is a slice indexing
   442  // operation slice[i] within a "for range slice" loop. The operation
   443  // could be explicit, such as slice[i] within (or even after) the
   444  // loop, or it could be implicit, such as "for i, v := range slice {}".
   445  // (These cannot be reliably distinguished.)
   446  func isRangeIndex(instr *ssa.IndexAddr) bool {
   447  	// Here we reverse-engineer the go/ssa lowering of range-over-slice:
   448  	//
   449  	//      n = len(x)
   450  	//      jump loop
   451  	// loop:                                                "rangeindex.loop"
   452  	//      phi = φ(-1, incr) #rangeindex
   453  	//      incr = phi + 1
   454  	//      cond = incr < n
   455  	//      if cond goto body else done
   456  	// body:                                                "rangeindex.body"
   457  	//      instr = &x[incr]
   458  	//      ...
   459  	// done:
   460  	if incr, ok := instr.Index.(*ssa.BinOp); ok && incr.Op == token.ADD {
   461  		if b := incr.Block(); b.Comment == "rangeindex.loop" {
   462  			if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok {
   463  				if cond := If.Cond.(*ssa.BinOp); cond.X == incr && cond.Op == token.LSS {
   464  					if call, ok := cond.Y.(*ssa.Call); ok {
   465  						common := call.Common()
   466  						if blt, ok := common.Value.(*ssa.Builtin); ok && blt.Name() == "len" {
   467  							return common.Args[0] == instr.X
   468  						}
   469  					}
   470  				}
   471  			}
   472  		}
   473  	}
   474  	return false
   475  }
   476  

View as plain text