From 4efd30c33b37571e45cc0a57397b61cc35701e0d Mon Sep 17 00:00:00 2001 From: Dominik Honnef Date: Sun, 10 May 2020 13:48:30 +0200 Subject: [PATCH] staticcheck: improve handling of byte slices and arrays in printf checker Closes gh-714 --- code/code.go | 1 + staticcheck/lint.go | 22 +++++++++++++++++-- .../testdata/src/CheckPrintf/CheckPrintf.go | 14 ++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/code/code.go b/code/code.go index 5ef7aef4d..73aebea60 100644 --- a/code/code.go +++ b/code/code.go @@ -101,6 +101,7 @@ func IsCallToAny(call *ir.CallCommon, names ...string) bool { return false } +// OPT(dh): IsType is kind of expensive; should we really use it? func IsType(T types.Type, name string) bool { return types.TypeString(T, nil) == name } func FilterDebug(instr []ir.Instruction) []ir.Instruction { diff --git a/staticcheck/lint.go b/staticcheck/lint.go index 282a60556..f16bf3a40 100644 --- a/staticcheck/lint.go +++ b/staticcheck/lint.go @@ -527,8 +527,26 @@ func checkPrintfCallImpl(carg *Argument, f ir.Value, args []ir.Value) { return true } - if flags&isString != 0 && (code.IsType(T, "[]byte") || isStringer(T, ms) || isError(T, ms)) { - return true + if flags&isString != 0 { + isStringyElem := func(typ types.Type) bool { + if typ, ok := typ.Underlying().(*types.Basic); ok { + return typ.Kind() == types.Byte + } + return false + } + switch T := T.(type) { + case *types.Slice: + if isStringyElem(T.Elem()) { + return true + } + case *types.Array: + if isStringyElem(T.Elem()) { + return true + } + } + if isStringer(T, ms) || isError(T, ms) { + return true + } } if flags&isPointer != 0 && code.IsPointerLike(T) { diff --git a/staticcheck/testdata/src/CheckPrintf/CheckPrintf.go b/staticcheck/testdata/src/CheckPrintf/CheckPrintf.go index 18a127251..8747a2930 100644 --- a/staticcheck/testdata/src/CheckPrintf/CheckPrintf.go +++ b/staticcheck/testdata/src/CheckPrintf/CheckPrintf.go @@ -399,3 +399,17 @@ func dbg(format string, args ...interface{}) { } fmt.Printf(format, args...) } + +// https://github.com/dominikh/go-tools/issues/714 +func fn2() { + type String string + type Byte byte + + var a string = "a" + var b []byte = []byte{'b'} + var c [1]byte = [1]byte{'c'} + var d String = "d" + var e []uint8 = []uint8{'e'} + var f []Byte = []Byte{'h'} + fmt.Printf("%s %s %s %s %s %s %s", a, b, c, &c, d, e, f) +}