github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa5007/sa5007.go (about) 1 package sa5007 2 3 import ( 4 "github.com/amarpal/go-tools/analysis/lint" 5 "github.com/amarpal/go-tools/analysis/report" 6 "github.com/amarpal/go-tools/go/ir" 7 "github.com/amarpal/go-tools/internal/passes/buildir" 8 9 "golang.org/x/tools/go/analysis" 10 ) 11 12 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 13 Analyzer: &analysis.Analyzer{ 14 Name: "SA5007", 15 Run: run, 16 Requires: []*analysis.Analyzer{buildir.Analyzer}, 17 }, 18 Doc: &lint.Documentation{ 19 Title: `Infinite recursive call`, 20 Text: `A function that calls itself recursively needs to have an exit 21 condition. Otherwise it will recurse forever, until the system runs 22 out of memory. 23 24 This issue can be caused by simple bugs such as forgetting to add an 25 exit condition. It can also happen "on purpose". Some languages have 26 tail call optimization which makes certain infinite recursive calls 27 safe to use. Go, however, does not implement TCO, and as such a loop 28 should be used instead.`, 29 Since: "2017.1", 30 Severity: lint.SeverityWarning, 31 MergeIf: lint.MergeIfAny, 32 }, 33 }) 34 35 var Analyzer = SCAnalyzer.Analyzer 36 37 func run(pass *analysis.Pass) (interface{}, error) { 38 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 39 eachCall(fn, func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) { 40 if callee != fn { 41 return 42 } 43 if _, ok := site.(*ir.Go); ok { 44 // Recursively spawning goroutines doesn't consume 45 // stack space infinitely, so don't flag it. 46 return 47 } 48 49 block := site.Block() 50 for _, b := range fn.Blocks { 51 if block.Dominates(b) { 52 continue 53 } 54 if len(b.Instrs) == 0 { 55 continue 56 } 57 if _, ok := b.Control().(*ir.Return); ok { 58 return 59 } 60 } 61 report.Report(pass, site, "infinite recursive call") 62 }) 63 } 64 return nil, nil 65 } 66 67 func eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) { 68 for _, b := range fn.Blocks { 69 for _, instr := range b.Instrs { 70 if site, ok := instr.(ir.CallInstruction); ok { 71 if g := site.Common().StaticCallee(); g != nil { 72 cb(fn, site, g) 73 } 74 } 75 } 76 } 77 }