From 4072608b58e32b761068538f254799669fdb9b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Mon, 29 Jan 2018 09:24:51 +0000 Subject: [PATCH] cmd/vet: %s is valid for an array of stringer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vet was quiet for []stringer, but not for [N]stringer. The source of the problem was how the recursive call used .Elem().Underlying() for arrays, but .Elem() for slices. In the first case, the named type is dropped, thus losing all information of attached methods. Be consistent across slices and arrays, by dropping the Underlying call that is causing trouble. Add regression tests too, including cases where the element type does not implement fmt.Stringer. Fixes #23552. Change-Id: I0fde07d101f112d5768be0a79207ef0b3dc45f2e Reviewed-on: https://go-review.googlesource.com/90455 Run-TryBot: Daniel Martí TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor Reviewed-by: Rob Pike --- src/cmd/vet/testdata/print.go | 35 +++++++++++++++++++++++------------ src/cmd/vet/types.go | 2 +- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/cmd/vet/testdata/print.go b/src/cmd/vet/testdata/print.go index b36abfc127..d7081a7303 100644 --- a/src/cmd/vet/testdata/print.go +++ b/src/cmd/vet/testdata/print.go @@ -130,8 +130,8 @@ func PrintfTests() { fmt.Printf("%U", x) // ERROR "Printf format %U has arg x of wrong type float64" fmt.Printf("%x", nil) // ERROR "Printf format %x has arg nil of wrong type untyped nil" fmt.Printf("%X", 2.3) // ERROR "Printf format %X has arg 2.3 of wrong type float64" - fmt.Printf("%s", stringerv) // ERROR "Printf format %s has arg stringerv of wrong type testdata.stringer" - fmt.Printf("%t", stringerv) // ERROR "Printf format %t has arg stringerv of wrong type testdata.stringer" + fmt.Printf("%s", stringerv) // ERROR "Printf format %s has arg stringerv of wrong type testdata.ptrStringer" + fmt.Printf("%t", stringerv) // ERROR "Printf format %t has arg stringerv of wrong type testdata.ptrStringer" fmt.Printf("%s", embeddedStringerv) // ERROR "Printf format %s has arg embeddedStringerv of wrong type testdata.embeddedStringer" fmt.Printf("%t", embeddedStringerv) // ERROR "Printf format %t has arg embeddedStringerv of wrong type testdata.embeddedStringer" fmt.Printf("%q", notstringerv) // ERROR "Printf format %q has arg notstringerv of wrong type testdata.notstringer" @@ -168,7 +168,7 @@ func PrintfTests() { Printf(format, "hi", "there") Printf(format, "hi") // ERROR "Printf format %s reads arg #2, but call has only 1 arg$" Printf("%s %d %.3v %q", "str", 4) // ERROR "Printf format %.3v reads arg #3, but call has only 2 args" - f := new(stringer) + f := new(ptrStringer) f.Warn(0, "%s", "hello", 3) // ERROR "Warn call has possible formatting directive %s" f.Warnf(0, "%s", "hello", 3) // ERROR "Warnf call needs 1 arg but has 2 args" f.Warnf(0, "%r", "hello") // ERROR "Warnf format %r has unknown verb r" @@ -352,25 +352,29 @@ func multi() []interface{} { panic("don't call - testing only") } -type stringer float64 +type stringer int -var stringerv stringer +func (stringer) String() string { return "string" } -func (*stringer) String() string { +type ptrStringer float64 + +var stringerv ptrStringer + +func (*ptrStringer) String() string { return "string" } -func (*stringer) Warn(int, ...interface{}) string { +func (*ptrStringer) Warn(int, ...interface{}) string { return "warn" } -func (*stringer) Warnf(int, string, ...interface{}) string { +func (*ptrStringer) Warnf(int, string, ...interface{}) string { return "warnf" } type embeddedStringer struct { foo string - stringer + ptrStringer bar int } @@ -479,13 +483,13 @@ type RecursiveStruct2 struct { var recursiveStruct1V = &RecursiveStruct1{} -// Issue 17798: unexported stringer cannot be formatted. +// Issue 17798: unexported ptrStringer cannot be formatted. type unexportedStringer struct { - t stringer + t ptrStringer } type unexportedStringerOtherFields struct { s string - t stringer + t ptrStringer S string } @@ -533,6 +537,13 @@ func UnexportedStringerOrError() { fmt.Println("foo\n") // ERROR "Println arg list ends with redundant newline" fmt.Println("foo\\n") // not an error fmt.Println(`foo\n`) // not an error + + intSlice := []int{3, 4} + fmt.Printf("%s", intSlice) // ERROR "Printf format %s has arg intSlice of wrong type \[\]int" + nonStringerArray := [1]unexportedStringer{{}} + fmt.Printf("%s", nonStringerArray) // ERROR "Printf format %s has arg nonStringerArray of wrong type \[1\]testdata.unexportedStringer" + fmt.Printf("%s", []stringer{3, 4}) // not an error + fmt.Printf("%s", [2]stringer{3, 4}) // not an error } // TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11. diff --git a/src/cmd/vet/types.go b/src/cmd/vet/types.go index 799dc655e6..cf785eed74 100644 --- a/src/cmd/vet/types.go +++ b/src/cmd/vet/types.go @@ -172,7 +172,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp return true // %s matches []byte } // Recur: []int matches %d. - return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress) + return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress) case *types.Slice: // Same as array.