Skip to content

Commit 6959087

Browse files
valyalarobpike
authored andcommitted
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/44831 Reviewed-by: Rob Pike <[email protected]>
1 parent c3189ce commit 6959087

File tree

2 files changed

+64
-6
lines changed

2 files changed

+64
-6
lines changed

src/cmd/vet/testdata/print.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ var notPercentDV notPercentDStruct
403403
type percentSStruct struct {
404404
a string
405405
b []byte
406-
c stringerarray
406+
C stringerarray
407407
}
408408

409409
var percentSV percentSStruct
@@ -472,3 +472,54 @@ func UnknownStructFprintln() {
472472
s := unknownStruct{}
473473
s.Fprintln(os.Stdout, "hello, world!") // OK
474474
}
475+
476+
// Issue 17798: unexported stringer cannot be formatted.
477+
type unexportedStringer struct {
478+
t stringer
479+
}
480+
type unexportedStringerOtherFields struct {
481+
s string
482+
t stringer
483+
S string
484+
}
485+
486+
// Issue 17798: unexported error cannot be formatted.
487+
type unexportedError struct {
488+
e error
489+
}
490+
type unexportedErrorOtherFields struct {
491+
s string
492+
e error
493+
S string
494+
}
495+
496+
type errorer struct{}
497+
498+
func (e errorer) Error() string { return "errorer" }
499+
500+
func UnexportedStringerOrError() {
501+
us := unexportedStringer{}
502+
fmt.Printf("%s", us) // ERROR "arg us for printf verb %s of wrong type"
503+
fmt.Printf("%s", &us) // ERROR "arg &us for printf verb %s of wrong type"
504+
505+
usf := unexportedStringerOtherFields{
506+
s: "foo",
507+
S: "bar",
508+
}
509+
fmt.Printf("%s", usf) // ERROR "arg usf for printf verb %s of wrong type"
510+
fmt.Printf("%s", &usf) // ERROR "arg &usf for printf verb %s of wrong type"
511+
512+
ue := unexportedError{
513+
e: &errorer{},
514+
}
515+
fmt.Printf("%s", ue) // ERROR "arg ue for printf verb %s of wrong type"
516+
fmt.Printf("%s", &ue) // ERROR "arg &ue for printf verb %s of wrong type"
517+
518+
uef := unexportedErrorOtherFields{
519+
s: "foo",
520+
e: &errorer{},
521+
S: "bar",
522+
}
523+
fmt.Printf("%s", uef) // ERROR "arg uef for printf verb %s of wrong type"
524+
fmt.Printf("%s", &uef) // ERROR "arg &uef for printf verb %s of wrong type"
525+
}

src/cmd/vet/types.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,8 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
134134
return true
135135
}
136136
// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
137-
if t&argString != 0 {
138-
if types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ) {
139-
return true
140-
}
137+
if t&argString != 0 && isConvertibleToString(typ) {
138+
return true
141139
}
142140

143141
typ = typ.Underlying()
@@ -261,6 +259,10 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
261259
return false
262260
}
263261

262+
func isConvertibleToString(typ types.Type) bool {
263+
return types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ)
264+
}
265+
264266
// hasBasicType reports whether x's type is a types.Basic with the given kind.
265267
func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool {
266268
t := f.pkg.types[x].Type
@@ -275,7 +277,12 @@ func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool {
275277
// type. For instance, with "%d" all the elements must be printable with the "%d" format.
276278
func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
277279
for i := 0; i < typ.NumFields(); i++ {
278-
if !f.matchArgTypeInternal(t, typ.Field(i).Type(), arg, inProgress) {
280+
typf := typ.Field(i)
281+
if !f.matchArgTypeInternal(t, typf.Type(), arg, inProgress) {
282+
return false
283+
}
284+
if t&argString != 0 && !typf.Exported() && isConvertibleToString(typf.Type()) {
285+
// Issue #17798: unexported Stringer or error cannot be properly fomatted.
279286
return false
280287
}
281288
}

0 commit comments

Comments
 (0)