honnef.co/go/tools@v0.5.0-0.dev.0.20240520180541-dcae280a5e87/staticcheck/sa1025/sa1025.go (about) 1 package sa1025 2 3 import ( 4 "go/types" 5 6 "honnef.co/go/tools/analysis/lint" 7 "honnef.co/go/tools/analysis/report" 8 "honnef.co/go/tools/go/ir" 9 "honnef.co/go/tools/go/ir/irutil" 10 "honnef.co/go/tools/internal/passes/buildir" 11 12 "golang.org/x/tools/go/analysis" 13 ) 14 15 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 16 Analyzer: &analysis.Analyzer{ 17 Name: "SA1025", 18 Run: run, 19 Requires: []*analysis.Analyzer{buildir.Analyzer}, 20 }, 21 Doc: &lint.Documentation{ 22 Title: `It is not possible to use \'(*time.Timer).Reset\''s return value correctly`, 23 Since: "2019.1", 24 Severity: lint.SeverityWarning, 25 MergeIf: lint.MergeIfAny, 26 }, 27 }) 28 29 var Analyzer = SCAnalyzer.Analyzer 30 31 func run(pass *analysis.Pass) (interface{}, error) { 32 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 33 for _, block := range fn.Blocks { 34 for _, ins := range block.Instrs { 35 call, ok := ins.(*ir.Call) 36 if !ok { 37 continue 38 } 39 if !irutil.IsCallTo(call.Common(), "(*time.Timer).Reset") { 40 continue 41 } 42 refs := call.Referrers() 43 if refs == nil { 44 continue 45 } 46 for _, ref := range irutil.FilterDebug(*refs) { 47 ifstmt, ok := ref.(*ir.If) 48 if !ok { 49 continue 50 } 51 52 found := false 53 for _, succ := range ifstmt.Block().Succs { 54 if len(succ.Preds) != 1 { 55 // Merge point, not a branch in the 56 // syntactical sense. 57 58 // FIXME(dh): this is broken for if 59 // statements a la "if x || y" 60 continue 61 } 62 irutil.Walk(succ, func(b *ir.BasicBlock) bool { 63 if !succ.Dominates(b) { 64 // We've reached the end of the branch 65 return false 66 } 67 for _, ins := range b.Instrs { 68 // TODO(dh): we should check that we're receiving from the 69 // channel of a time.Timer to further reduce false 70 // positives. Not a key priority, considering the rarity 71 // of Reset and the tiny likeliness of a false positive 72 // 73 // We intentionally don't handle aliases here, because 74 // we're only interested in time.Timer.C. 75 if ins, ok := ins.(*ir.Recv); ok && types.TypeString(ins.Chan.Type(), nil) == "<-chan time.Time" { 76 found = true 77 return false 78 } 79 } 80 return true 81 }) 82 } 83 84 if found { 85 report.Report(pass, call, "it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring") 86 } 87 } 88 } 89 } 90 } 91 return nil, nil 92 }