src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

sa5007.go (1974B)


      1 package sa5007
      2 
      3 import (
      4 	"honnef.co/go/tools/analysis/lint"
      5 	"honnef.co/go/tools/analysis/report"
      6 	"honnef.co/go/tools/go/ir"
      7 	"honnef.co/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.RawDocumentation{
     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) (any, 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.Exits() {
     51 				if block.Dominates(b) {
     52 					continue
     53 				}
     54 				return
     55 			}
     56 			report.Report(pass, site, "infinite recursive call")
     57 		})
     58 	}
     59 	return nil, nil
     60 }
     61 
     62 func eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) {
     63 	for _, b := range fn.Blocks {
     64 		for _, instr := range b.Instrs {
     65 			if site, ok := instr.(ir.CallInstruction); ok {
     66 				if g := site.Common().StaticCallee(); g != nil {
     67 					cb(fn, site, g)
     68 				}
     69 			}
     70 		}
     71 	}
     72 }