github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa4030/sa4030.go (about) 1 package sa4030 2 3 import ( 4 "fmt" 5 "go/ast" 6 7 "github.com/amarpal/go-tools/analysis/code" 8 "github.com/amarpal/go-tools/analysis/lint" 9 "github.com/amarpal/go-tools/analysis/report" 10 "github.com/amarpal/go-tools/pattern" 11 12 "golang.org/x/tools/go/analysis" 13 "golang.org/x/tools/go/analysis/passes/inspect" 14 ) 15 16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 17 Analyzer: &analysis.Analyzer{ 18 Name: "SA4030", 19 Run: run, 20 Requires: []*analysis.Analyzer{inspect.Analyzer}, 21 }, 22 Doc: &lint.Documentation{ 23 Title: "Ineffective attempt at generating random number", 24 Text: ` 25 Functions in the \'math/rand\' package that accept upper limits, such 26 as \'Intn\', generate random numbers in the half-open interval [0,n). In 27 other words, the generated numbers will be \'>= 0\' and \'< n\' – they 28 don't include \'n\'. \'rand.Intn(1)\' therefore doesn't generate \'0\' 29 or \'1\', it always generates \'0\'.`, 30 Since: "2022.1", 31 Severity: lint.SeverityWarning, 32 MergeIf: lint.MergeIfAny, 33 }, 34 }) 35 36 var Analyzer = SCAnalyzer.Analyzer 37 38 var ineffectiveRandIntQ = pattern.MustParse(` 39 (CallExpr 40 (Symbol 41 name@(Or 42 "math/rand.Int31n" 43 "math/rand.Int63n" 44 "math/rand.Intn" 45 "(*math/rand.Rand).Int31n" 46 "(*math/rand.Rand).Int63n" 47 "(*math/rand.Rand).Intn")) 48 [(IntegerLiteral "1")])`) 49 50 func run(pass *analysis.Pass) (interface{}, error) { 51 fn := func(node ast.Node) { 52 m, ok := code.Match(pass, ineffectiveRandIntQ, node) 53 if !ok { 54 return 55 } 56 57 report.Report(pass, node, 58 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", 59 m.State["name"], report.Render(pass, node))) 60 } 61 62 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 63 return nil, nil 64 }