Repository navigation
x/tools/gopls: suggest rand.N for time.Duration random generation #81323
Description
gopls version
Build info
golang.org/x/tools/gopls v0.20.0
golang.org/x/tools/gopls@v0.20.0 h1:fxOYZXKl6IsOTKIh6IgjDbIDHlr5btOtOUkrGOgFDB4=
github.com/BurntSushi/toml@v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/fatih/camelcase@v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/gomodifytags@v1.17.1-0.20250423142747-f3939df9aa3c h1:dDSgAjoOMp8da3egfz0t2S+t8RGOpEmEXZubcGuc0Bg=
github.com/fatih/structtag@v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fsnotify/fsnotify@v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/google/go-cmp@v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
golang.org/x/exp/typeparams@v0.0.0-20250620022241-b7579e27df2b h1:KdrhdYPDUvJTvrDK9gdjfFd6JTk8vA1WJoldYSi0kHo=
golang.org/x/mod@v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/sync@v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sys@v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/telemetry@v0.0.0-20250710130107-8d8967aff50b h1:DU+gwOBXU+6bO0sEyO7o/NeMlxZxCZEvI7v+J4a1zRQ=
golang.org/x/text@v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/tools@v0.35.1-0.20250728180453-01a3475a31bc h1:ZRKyKRJl/YEWl9ScZwd6Ua6xSt7DE6tHp1I3ucMroGM=
golang.org/x/vuln@v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I=
honnef.co/go/tools@v0.7.0-0.dev.0.20250523013057-bbc2f4dd71ea h1:fj8r9irJSpolAGUdZBxJIRY3lLc4jH2Dt4lwnWyWwpw=
mvdan.cc/gofumpt@v0.8.0 h1:nZUCeC2ViFaerTcYKstMmfysj6uhQrA2vJe+2vwGU6k=
mvdan.cc/xurls/v2@v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
go: go1.25.0
go env
AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN='/Users/amirali/go/bin'
GOCACHE='/Users/amirali/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/amirali/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT='jsonv2'
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/7j/t938qv7d49d134ywtsj94m9c0000gn/T/go-build2206170510=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/amirali/Projects/randfix/go.mod'
GOMODCACHE='/Users/amirali/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPACKAGESDRIVER=''
GOPATH='/Users/amirali/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.27.0/libexec'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/amirali/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.27.0/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.27.0'
GOWORK=''
PKG_CONFIG='pkg-config'What did you do?
I was refactoring some code which used old math/rand into newer math/rand/v2 which generated random time durations for testing. But after refactoring, we can leverage the rand.IntN generics and multiplying it by a time unit:
package main
import (
"math/rand/v2"
"time"
)
func main() {
// Current pattern (clunky, requires casting)
_ = time.Duration(rand.Int64N(int64(5 * time.Second)))
// Modernized pattern
_ = rand.N(5 * time.Second)
}
What did you see happen?
Nothing
What did you expect to see?
I expected the modernize analyzer (and by extension, gopls quick fixes) to suggest replacing the clunky pattern with the cleaner, idiomatic rand.N generic function, since time.Duration is an int64 under the hood and is fully supported by rand.N[T].
I got this idea from reading https://go.dev/blog/randv2 blog, a real use case can be viewed in this PR nats-io/nats-server#8557.
Editor and settings
No response
Logs
No response
Activity
gabyhelp commentedon Sep 2, 2026
Related Issues
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
gopherbot commentedon Sep 3, 2026
Change https://go.dev/cl/826944 mentions this issue:
go/analysis/passes/modernize: suggest rand.N for durationsadonovan commentedon Sep 3, 2026
This seems like a reasonable idea; my only concern is that it it's a pretty obscure pattern than might not turn up many hits. If the diagnostic is very accurate and the fix very reliable, that still might be acceptable so long as the computation is cheap. That means the implementation should reject candidate packages as efficiently as possible, for example those that don't import both
timeandmath/rand, without visiting syntax.@madelinekalil is going to run it over the corpus to see what it turns up.
madelinekalil commentedon Sep 8, 2026
I ran this analyzer pattern over ~30,000 modules from the module mirror corpus and it yielded 54 unique occurrences. Some examples:
"\t\t// Backoff linearly, adding some jitter.\n\t\twait += r.backoff + time.Duration(rand.Int64N(int64(r.backoff)))\n\t}"
"\thalf := d / 2\n\treturn half + time.Duration(rand.Int64N(int64(d-half)))\n}"
This pattern is uncommon, so I'm not yet sure whether we want to accept this. I'll take a look at the CL