lab.nexedi.com will be down from Thursday, 20 March 2025, 07:30:00 UTC for a duration of approximately 2 hours

Commit 4072608b authored by Daniel Martí's avatar Daniel Martí Committed by Ian Lance Taylor

cmd/vet: %s is valid for an array of stringer

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í <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
Reviewed-by: default avatarRob Pike <r@golang.org>
parent 1f85917f
...@@ -130,8 +130,8 @@ func PrintfTests() { ...@@ -130,8 +130,8 @@ func PrintfTests() {
fmt.Printf("%U", x) // ERROR "Printf format %U has arg x of wrong type float64" 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", 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("%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("%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.stringer" 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("%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("%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" fmt.Printf("%q", notstringerv) // ERROR "Printf format %q has arg notstringerv of wrong type testdata.notstringer"
...@@ -168,7 +168,7 @@ func PrintfTests() { ...@@ -168,7 +168,7 @@ func PrintfTests() {
Printf(format, "hi", "there") Printf(format, "hi", "there")
Printf(format, "hi") // ERROR "Printf format %s reads arg #2, but call has only 1 arg$" 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" 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.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, "%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" f.Warnf(0, "%r", "hello") // ERROR "Warnf format %r has unknown verb r"
...@@ -352,25 +352,29 @@ func multi() []interface{} { ...@@ -352,25 +352,29 @@ func multi() []interface{} {
panic("don't call - testing only") 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" return "string"
} }
func (*stringer) Warn(int, ...interface{}) string { func (*ptrStringer) Warn(int, ...interface{}) string {
return "warn" return "warn"
} }
func (*stringer) Warnf(int, string, ...interface{}) string { func (*ptrStringer) Warnf(int, string, ...interface{}) string {
return "warnf" return "warnf"
} }
type embeddedStringer struct { type embeddedStringer struct {
foo string foo string
stringer ptrStringer
bar int bar int
} }
...@@ -479,13 +483,13 @@ type RecursiveStruct2 struct { ...@@ -479,13 +483,13 @@ type RecursiveStruct2 struct {
var recursiveStruct1V = &RecursiveStruct1{} var recursiveStruct1V = &RecursiveStruct1{}
// Issue 17798: unexported stringer cannot be formatted. // Issue 17798: unexported ptrStringer cannot be formatted.
type unexportedStringer struct { type unexportedStringer struct {
t stringer t ptrStringer
} }
type unexportedStringerOtherFields struct { type unexportedStringerOtherFields struct {
s string s string
t stringer t ptrStringer
S string S string
} }
...@@ -533,6 +537,13 @@ func UnexportedStringerOrError() { ...@@ -533,6 +537,13 @@ func UnexportedStringerOrError() {
fmt.Println("foo\n") // ERROR "Println arg list ends with redundant newline" 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
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. // TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11.
......
...@@ -172,7 +172,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp ...@@ -172,7 +172,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
return true // %s matches []byte return true // %s matches []byte
} }
// Recur: []int matches %d. // 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: case *types.Slice:
// Same as array. // Same as array.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment