type percentSStruct struct {
a string
b []byte
- c stringerarray
+ C stringerarray
}
var percentSV percentSStruct
s := unknownStruct{}
s.Fprintln(os.Stdout, "hello, world!") // OK
}
+
+// Issue 17798: unexported stringer cannot be formatted.
+type unexportedStringer struct {
+ t stringer
+}
+type unexportedStringerOtherFields struct {
+ s string
+ t stringer
+ S string
+}
+
+// Issue 17798: unexported error cannot be formatted.
+type unexportedError struct {
+ e error
+}
+type unexportedErrorOtherFields struct {
+ s string
+ e error
+ S string
+}
+
+type errorer struct{}
+
+func (e errorer) Error() string { return "errorer" }
+
+func UnexportedStringerOrError() {
+ us := unexportedStringer{}
+ fmt.Printf("%s", us) // ERROR "arg us for printf verb %s of wrong type"
+ fmt.Printf("%s", &us) // ERROR "arg &us for printf verb %s of wrong type"
+
+ usf := unexportedStringerOtherFields{
+ s: "foo",
+ S: "bar",
+ }
+ fmt.Printf("%s", usf) // ERROR "arg usf for printf verb %s of wrong type"
+ fmt.Printf("%s", &usf) // ERROR "arg &usf for printf verb %s of wrong type"
+
+ ue := unexportedError{
+ e: &errorer{},
+ }
+ fmt.Printf("%s", ue) // ERROR "arg ue for printf verb %s of wrong type"
+ fmt.Printf("%s", &ue) // ERROR "arg &ue for printf verb %s of wrong type"
+
+ uef := unexportedErrorOtherFields{
+ s: "foo",
+ e: &errorer{},
+ S: "bar",
+ }
+ fmt.Printf("%s", uef) // ERROR "arg uef for printf verb %s of wrong type"
+ fmt.Printf("%s", &uef) // ERROR "arg &uef for printf verb %s of wrong type"
+}
return true
}
// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
- if t&argString != 0 {
- if types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ) {
- return true
- }
+ if t&argString != 0 && isConvertibleToString(typ) {
+ return true
}
typ = typ.Underlying()
return false
}
+func isConvertibleToString(typ types.Type) bool {
+ return types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ)
+}
+
// hasBasicType reports whether x's type is a types.Basic with the given kind.
func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool {
t := f.pkg.types[x].Type
// type. For instance, with "%d" all the elements must be printable with the "%d" format.
func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
for i := 0; i < typ.NumFields(); i++ {
- if !f.matchArgTypeInternal(t, typ.Field(i).Type(), arg, inProgress) {
+ typf := typ.Field(i)
+ if !f.matchArgTypeInternal(t, typf.Type(), arg, inProgress) {
+ return false
+ }
+ if t&argString != 0 && !typf.Exported() && isConvertibleToString(typf.Type()) {
+ // Issue #17798: unexported Stringer or error cannot be properly fomatted.
return false
}
}