src

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

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 }