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