Repository navigation
Description
SA5009 reports that fmt.Sprintf receives zero arguments when a slice is populated in a loop and passed using ....
The code executes correctly with both Go 1.24.0 and Go 1.27.2. Staticcheck 2026.1 does not report this diagnostic, while 2026.2.1 and 2026.2.2 do.
Minimal reproduction
go.mod:
module example.com/sa5009-repro
go 1.24.0
repro_test.go:
package repro
import (
"fmt"
"testing"
)
func TestLoopSlice(t *testing.T) {
v := make([]interface{}, 2)
for i := 0; i < len(v); i++ {
v[i] = 100.0
}
got := fmt.Sprintf("%f %f", v...)
if got != "100.000000 100.000000" {
t.Fatal(got)
}
}Commands and output
$ go test -v -count=1 .
=== RUN TestLoopSlice
--- PASS: TestLoopSlice (0.00s)
PASS
ok example.com/sa5009-repro 0.002s
$ staticcheck -checks SA5009 .
repro_test.go:13:21: Printf format %f reads arg #1, but call has only 0 args (SA5009)
Expected behavior
No SA5009 diagnostic. The slice has length 2, both elements contain float64 values, and expanding it supplies two arguments matching the two %f verbs.
Versions tested
| Go used for analysis | Staticcheck | Result |
|---|---|---|
| 1.24.0 | 2026.1 (v0.7.0) | No diagnostic |
| 1.24.0 | 2026.2.1 (v0.8.1) | False-positive SA5009 |
| 1.24.0 | 2026.2.2 (v0.8.2) | False-positive SA5009 |
| 1.27.2 | 2026.2.2 (v0.8.2) | False-positive SA5009 |
I also rebuilt Staticcheck 2026.1 with Go 1.27.2 and confirmed that it still does not report SA5009 on the original affected test when analyzing with Go 1.24.0. This suggests a regression in Staticcheck rather than an issue caused solely by the Go version used to build it.
The minimal reproduction uses only the standard library.
Environment
The environment below is for the Go 1.24.0 reproduction. The Staticcheck binary itself was built with Go 1.27.2.
staticcheck -version:
staticcheck 2026.2.2 (0.8.2)
staticcheck -debug.version:
staticcheck 2026.2.2 (0.8.2)
Compiled with Go version: go1.27.2
Main module:
honnef.co/go/tools@v0.8.2 (sum: h1:+jPbCzuYxYrcoOvCecYUFDj+XJQRmzIhJmlbpps21Zk=)
Dependencies:
github.com/BurntSushi/toml@v1.4.1-0.20240526193622-a339e1f7089c (sum: h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=)
golang.org/x/exp/typeparams@v0.0.0-20231108232855-2478ac86f678 (sum: h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=)
golang.org/x/mod@v0.41.0 (sum: h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=)
golang.org/x/sync@v0.23.0 (sum: h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=)
golang.org/x/tools@v0.51.0 (sum: h1:k4Xc/1Om9jwkBJBo4NVLMSARBoWtK10mx+W5BnXCeAI=)
go version:
go version go1.24.0 linux/amd64
go env (local paths anonymized):
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='<HOME>/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='<HOME>/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=<REPRO_TEMP_DIR>/go-build2738305508=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='<REPRO_DIR>/go.mod'
GOMODCACHE='<HOME>/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='<HOME>/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='<HOME>/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='<HOME>/.config/go/telemetry'
GOTMPDIR='<REPRO_TEMP_DIR>'
GOTOOLCHAIN='local'
GOTOOLDIR='<HOME>/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.0'
GOWORK=''
PKG_CONFIG='pkg-config'
Activity