Skip to content

Commit 36563e2

Browse files
reillywatsonbradfitz
authored andcommitted
cmd/vet: verify potentially-recursive Stringers are actually Stringers
The printf recursiveStringer check was checking for a function called String(), but wasn't checking that it matched the actual function signature of Stringer. Fixes golang/go#30441 Change-Id: I09d5fba035bb717036f7edf57efc63e2e3fe51d5 Reviewed-on: https://go-review.googlesource.com/c/tools/+/164217 Reviewed-by: Alan Donovan <[email protected]>
1 parent 9e44c1c commit 36563e2

File tree

2 files changed

+26
-4
lines changed

2 files changed

+26
-4
lines changed

go/analysis/passes/printf/printf.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -856,20 +856,28 @@ func recursiveStringer(pass *analysis.Pass, e ast.Expr) bool {
856856
return false
857857
}
858858

859-
// Is it the receiver r, or &r?
860-
recv := stringMethod.Type().(*types.Signature).Recv()
861-
if recv == nil {
859+
sig := stringMethod.Type().(*types.Signature)
860+
if !isStringer(sig) {
862861
return false
863862
}
863+
864+
// Is it the receiver r, or &r?
864865
if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND {
865866
e = u.X // strip off & from &r
866867
}
867868
if id, ok := e.(*ast.Ident); ok {
868-
return pass.TypesInfo.Uses[id] == recv
869+
return pass.TypesInfo.Uses[id] == sig.Recv()
869870
}
870871
return false
871872
}
872873

874+
// isStringer reports whether the method signature matches the String() definition in fmt.Stringer.
875+
func isStringer(sig *types.Signature) bool {
876+
return sig.Params().Len() == 0 &&
877+
sig.Results().Len() == 1 &&
878+
sig.Results().At(0).Type() == types.Typ[types.String]
879+
}
880+
873881
// isFunctionValue reports whether the expression is a function as opposed to a function call.
874882
// It is almost always a mistake to print a function value.
875883
func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {

go/analysis/passes/printf/testdata/src/a/a.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,20 @@ func (p *recursivePtrStringer) String() string {
516516
return fmt.Sprintln(p) // want "Sprintln arg p causes recursive call to String method"
517517
}
518518

519+
// implements a String() method but with non-matching return types
520+
type nonStringerWrongReturn int
521+
522+
func (s nonStringerWrongReturn) String() (string, error) {
523+
return "", fmt.Errorf("%v", s)
524+
}
525+
526+
// implements a String() method but with non-matching arguments
527+
type nonStringerWrongArgs int
528+
529+
func (s nonStringerWrongArgs) String(i int) string {
530+
return fmt.Sprintf("%d%v", i, s)
531+
}
532+
519533
type cons struct {
520534
car int
521535
cdr *cons

0 commit comments

Comments
 (0)