sa4030.go raw
1 package sa4030
2
3 import (
4 "fmt"
5 "go/ast"
6
7 "honnef.co/go/tools/analysis/code"
8 "honnef.co/go/tools/analysis/lint"
9 "honnef.co/go/tools/analysis/report"
10 "honnef.co/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.RawDocumentation{
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 }
65