github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa1017/sa1017.go (about)

     1  package sa1017
     2  
     3  import (
     4  	"go/constant"
     5  
     6  	"github.com/amarpal/go-tools/analysis/callcheck"
     7  	"github.com/amarpal/go-tools/analysis/lint"
     8  	"github.com/amarpal/go-tools/go/ir"
     9  	"github.com/amarpal/go-tools/internal/passes/buildir"
    10  	"github.com/amarpal/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.Documentation{
    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  }