sa4030.go (1928B)
1 package sa4030 2 3 import ( 4 "fmt" 5 6 "honnef.co/go/tools/analysis/code" 7 "honnef.co/go/tools/analysis/lint" 8 "honnef.co/go/tools/analysis/report" 9 "honnef.co/go/tools/pattern" 10 11 "golang.org/x/tools/go/analysis" 12 ) 13 14 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 15 Analyzer: &analysis.Analyzer{ 16 Name: "SA4030", 17 Run: run, 18 Requires: code.RequiredAnalyzers, 19 }, 20 Doc: &lint.RawDocumentation{ 21 Title: "Ineffective attempt at generating random number", 22 Text: ` 23 Functions in the \'math/rand\' package that accept upper limits, such 24 as \'Intn\', generate random numbers in the half-open interval [0,n). In 25 other words, the generated numbers will be \'>= 0\' and \'< n\' – they 26 don't include \'n\'. \'rand.Intn(1)\' therefore doesn't generate \'0\' 27 or \'1\', it always generates \'0\'.`, 28 Since: "2022.1", 29 Severity: lint.SeverityWarning, 30 MergeIf: lint.MergeIfAny, 31 }, 32 }) 33 34 var Analyzer = SCAnalyzer.Analyzer 35 36 var ineffectiveRandIntQ = pattern.MustParse(` 37 (CallExpr 38 (Symbol 39 name@(Or 40 "math/rand.Int31n" 41 "math/rand.Int63n" 42 "math/rand.Intn" 43 "(*math/rand.Rand).Int31n" 44 "(*math/rand.Rand).Int63n" 45 "(*math/rand.Rand).Intn" 46 47 "math/rand/v2.Int32N" 48 "math/rand/v2.Int64N" 49 "math/rand/v2.IntN" 50 "math/rand/v2.N" 51 "math/rand/v2.Uint32N" 52 "math/rand/v2.Uint64N" 53 "math/rand/v2.UintN" 54 55 "(*math/rand/v2.Rand).Int32N" 56 "(*math/rand/v2.Rand).Int64N" 57 "(*math/rand/v2.Rand).IntN" 58 "(*math/rand/v2.Rand).Uint32N" 59 "(*math/rand/v2.Rand).Uint64N" 60 "(*math/rand/v2.Rand).UintN")) 61 [(IntegerLiteral "1")])`) 62 63 func run(pass *analysis.Pass) (any, error) { 64 for node, m := range code.Matches(pass, ineffectiveRandIntQ) { 65 report.Report(pass, node, 66 fmt.Sprintf("%s(n) generates a random value 0 <= x < n; that is, the generated values don't include n; %s therefore always returns 0", 67 m.State["name"], report.Render(pass, node))) 68 } 69 return nil, nil 70 }