Commit 6959087b authored by Aliaksandr Valialkin's avatar Aliaksandr Valialkin Committed by Rob Pike

cmd/vet: unexported Stringer and error fields cannot be formatted

According to CL 31817, fmt cannot invoke String or Error methods
on unexported struct fields.

Fixes #17798.

Change-Id: I0d516577298bc36daa9a94313c3874d64dc079e6
Reviewed-on: https://go-review.googlesource.com/44831Reviewed-by: default avatarRob Pike <r@golang.org>
parent c3189cee
......@@ -403,7 +403,7 @@ var notPercentDV notPercentDStruct
type percentSStruct struct {
a string
b []byte
c stringerarray
C stringerarray
}
var percentSV percentSStruct
......@@ -472,3 +472,54 @@ func UnknownStructFprintln() {
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"
}
......@@ -134,10 +134,8 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
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()
......@@ -261,6 +259,10 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
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
......@@ -275,7 +277,12 @@ func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool {
// 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
}
}
......
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