Go version
go version go1.24.5 linux/amd64
Output of go env
in your module/workspace:
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/ubuntu/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/ubuntu/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3272950330=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/tmp/example/go.mod'
GOMODCACHE='/home/ubuntu/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/ubuntu/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/ubuntu/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.5.linux-amd64'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/ubuntu/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/ubuntu/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.5.linux-amd64/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.24.5'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
I tried running go vet
on the following code and expected no errors:
// example1.go
package example
import "fmt"
func ConditionallyFormattedMessage(message string, args ...interface{}) string {
if len(args) == 0 {
return message
} else {
return fmt.Sprintf(message, args...)
}
}
func Example1(arg string) string {
return ConditionallyFormattedMessage(arg)
}
What did you see happen?
Running go vet
on the above code results in a printf problem being reported despite there being none:
$ go vet
# example.com/m
./example1.go:15:46: non-constant format string in call to example.com/m.ConditionallyFormattedMessage
What did you expect to see?
I expected no errors.
Additional context
The above is a minimal reproduction of an issue I'm seeing with methods in the (quite popular) errorx
package such as (*github.com/joomcode/errorx.Type).New
and (*github.com/joomcode/errorx.Type).Wrap
. The errorx
package has a method that is very similar to the ConditionallyFormattedMessage()
function above - ErrorBuilder.WithConditionallyFormattedMessage()
:
https://github.com/joomcode/errorx/blob/3280086cb5400e2900300ba010ddf658fa717fc4/builder.go#L84-L95
which is used by the aforementioned New
and Wrap
methods:
https://github.com/joomcode/errorx/blob/3280086cb5400e2900300ba010ddf658fa717fc4/type.go#L41-L45
https://github.com/joomcode/errorx/blob/3280086cb5400e2900300ba010ddf658fa717fc4/type.go#L60-L65
An alternative reproduction using github.com/joomcode/errorx@v1.2.0
can be seen below:
// example2.go
package example
import "github.com/joomcode/errorx"
func Example2(arg string) *errorx.Error {
return errorx.InternalError.New(arg)
}
$ go vet
# example.com/m
./example2.go:7:34: non-constant format string in call to (*github.com/joomcode/errorx.Type).New
Comment From: gabyhelp
Related Issues
- x/tools/passes/printf: conditionally printf-like functions are treated as printf-like, causing false positives #73485 (closed)
- cmd/vet: printf analyzer cannot analyze anonymous functions #44350
- x/tools/go/analysis/passes/printf: `%#x` recursion is not recognized #73825 (closed)
- x/tools/gopls: doesn't seem to vet text output formatting errors in non-core packages #34059 (closed)
- x/tools/analysis: printf pass should only allow %w in fmt.Errorf #37792 (closed)
- go vet incorrectly recognizes format verb in string #38131 (closed)
- cmd/vet: Formatting string check cannot see through type assertions or varargs #70814 (closed)
- go1.13 errors package #34060 (closed)
- cmd/doc: garbled -src output #37028 (closed)
- cmd/vet: doesn't handle formatting strings in variables #44063
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: cagedmantis
Comment From: adonovan
The tacit assumption of the printf checker is that "conditionally printf" is not a great way to design APIs. For example, ConditionallyFormattedMessage("100%!") returns "100%!", but as soon as you add arguments, the interpretation of the string becomes non-literal, which is potentially surprising. So my advice is: eliminate the special case for zero args. And add an "f" suffix to the function.
However, you're not the first person to run into this issue, so we have at least a problem of inadequate documentation. We can fix that.