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:
parent
9e44c1c403
commit
36563e24a2
|
@ -856,20 +856,28 @@ func recursiveStringer(pass *analysis.Pass, e ast.Expr) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is it the receiver r, or &r?
|
sig := stringMethod.Type().(*types.Signature)
|
||||||
recv := stringMethod.Type().(*types.Signature).Recv()
|
if !isStringer(sig) {
|
||||||
if recv == nil {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is it the receiver r, or &r?
|
||||||
if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND {
|
if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND {
|
||||||
e = u.X // strip off & from &r
|
e = u.X // strip off & from &r
|
||||||
}
|
}
|
||||||
if id, ok := e.(*ast.Ident); ok {
|
if id, ok := e.(*ast.Ident); ok {
|
||||||
return pass.TypesInfo.Uses[id] == recv
|
return pass.TypesInfo.Uses[id] == sig.Recv()
|
||||||
}
|
}
|
||||||
return false
|
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.
|
// 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.
|
// It is almost always a mistake to print a function value.
|
||||||
func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
|
func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
|
||||||
|
|
|
@ -516,6 +516,20 @@ func (p *recursivePtrStringer) String() string {
|
||||||
return fmt.Sprintln(p) // want "Sprintln arg p causes recursive call to String method"
|
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 {
|
type cons struct {
|
||||||
car int
|
car int
|
||||||
cdr *cons
|
cdr *cons
|
||||||
|
|
Loading…
Reference in New Issue