1
0
mirror of https://github.com/golang/go synced 2024-11-18 22:24:50 -07:00

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 <adonovan@google.com>
This commit is contained in:
Reilly Watson 2019-02-27 14:53:29 -05:00 committed by Brad Fitzpatrick
parent 9e44c1c403
commit 36563e24a2
2 changed files with 26 additions and 4 deletions

View File

@ -856,20 +856,28 @@ func recursiveStringer(pass *analysis.Pass, e ast.Expr) bool {
return false
}
// Is it the receiver r, or &r?
recv := stringMethod.Type().(*types.Signature).Recv()
if recv == nil {
sig := stringMethod.Type().(*types.Signature)
if !isStringer(sig) {
return false
}
// Is it the receiver r, or &r?
if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND {
e = u.X // strip off & from &r
}
if id, ok := e.(*ast.Ident); ok {
return pass.TypesInfo.Uses[id] == recv
return pass.TypesInfo.Uses[id] == sig.Recv()
}
return false
}
// isStringer reports whether the method signature matches the String() definition in fmt.Stringer.
func isStringer(sig *types.Signature) bool {
return sig.Params().Len() == 0 &&
sig.Results().Len() == 1 &&
sig.Results().At(0).Type() == types.Typ[types.String]
}
// isFunctionValue reports whether the expression is a function as opposed to a function call.
// It is almost always a mistake to print a function value.
func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {

View File

@ -516,6 +516,20 @@ func (p *recursivePtrStringer) String() string {
return fmt.Sprintln(p) // want "Sprintln arg p causes recursive call to String method"
}
// implements a String() method but with non-matching return types
type nonStringerWrongReturn int
func (s nonStringerWrongReturn) String() (string, error) {
return "", fmt.Errorf("%v", s)
}
// implements a String() method but with non-matching arguments
type nonStringerWrongArgs int
func (s nonStringerWrongArgs) String(i int) string {
return fmt.Sprintf("%d%v", i, s)
}
type cons struct {
car int
cdr *cons