sa1017.go raw

   1  package sa1017
   2  
   3  import (
   4  	"go/constant"
   5  
   6  	"honnef.co/go/tools/analysis/callcheck"
   7  	"honnef.co/go/tools/analysis/lint"
   8  	"honnef.co/go/tools/go/ir"
   9  	"honnef.co/go/tools/internal/passes/buildir"
  10  	"honnef.co/go/tools/knowledge"
  11  
  12  	"golang.org/x/tools/go/analysis"
  13  )
  14  
  15  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  16  	Analyzer: &analysis.Analyzer{
  17  		Name:     "SA1017",
  18  		Requires: []*analysis.Analyzer{buildir.Analyzer},
  19  		Run:      callcheck.Analyzer(rules),
  20  	},
  21  	Doc: &lint.RawDocumentation{
  22  		Title: `Channels used with \'os/signal.Notify\' should be buffered`,
  23  		Text: `The \'os/signal\' package uses non-blocking channel sends when delivering
  24  signals. If the receiving end of the channel isn't ready and the
  25  channel is either unbuffered or full, the signal will be dropped. To
  26  avoid missing signals, the channel should be buffered and of the
  27  appropriate size. For a channel used for notification of just one
  28  signal value, a buffer of size 1 is sufficient.`,
  29  		Since:    "2017.1",
  30  		Severity: lint.SeverityWarning,
  31  		MergeIf:  lint.MergeIfAny,
  32  	},
  33  })
  34  
  35  var Analyzer = SCAnalyzer.Analyzer
  36  
  37  var rules = map[string]callcheck.Check{
  38  	"os/signal.Notify": func(call *callcheck.Call) {
  39  		arg := call.Args[knowledge.Arg("os/signal.Notify.c")]
  40  		if isUnbufferedChannel(arg.Value) {
  41  			arg.Invalid("the channel used with signal.Notify should be buffered")
  42  		}
  43  	},
  44  }
  45  
  46  func isUnbufferedChannel(v callcheck.Value) bool {
  47  	// TODO(dh): this check of course misses many cases of unbuffered
  48  	// channels, such as any in phi or sigma nodes. We'll eventually
  49  	// replace this function.
  50  	val := v.Value
  51  	if ct, ok := val.(*ir.ChangeType); ok {
  52  		val = ct.X
  53  	}
  54  	mk, ok := val.(*ir.MakeChan)
  55  	if !ok {
  56  		return false
  57  	}
  58  	if k, ok := mk.Size.(*ir.Const); ok && k.Value.Kind() == constant.Int {
  59  		if v, ok := constant.Int64Val(k.Value); ok && v == 0 {
  60  			return true
  61  		}
  62  	}
  63  	return false
  64  }
  65