1
0
mirror of https://github.com/golang/go synced 2024-11-12 09:20:22 -07:00

cmd/vet: diagnose using Printf on a function value

Printing a function value is nearly useless outside of debugging, but
can occur by mistake when one forgets to call it. Diagnose this.

I did this myself just the other day and it arose in cl/14031.
Easy to fix and seems worthwhile.

Fixes #12295.

Change-Id: Ice125a84559f0394f7fa7272b5d31ae602b07f83
Reviewed-on: https://go-review.googlesource.com/14122
Reviewed-by: Andrew Gerrand <adg@golang.org>
This commit is contained in:
Rob Pike 2015-08-31 14:36:36 -07:00
parent 5f2cda5867
commit 43a7a9cf43
2 changed files with 27 additions and 3 deletions

View File

@ -447,6 +447,10 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
arg := call.Args[argNum]
if !f.matchArgType(v.typ, nil, arg) {
typeString := ""
if f.isFunctionValue(arg) {
f.Badf(call.Pos(), "arg %s in printf call is a function value, not a function call", f.gofmt(arg))
return false
}
if typ := f.pkg.types[arg].Type; typ != nil {
typeString = typ.String()
}
@ -490,6 +494,16 @@ func (f *File) recursiveStringer(e ast.Expr) bool {
return f.stringers[obj]
}
// 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 (f *File) isFunctionValue(e ast.Expr) bool {
if typ := f.pkg.types[e].Type; typ != nil {
_, ok := typ.(*types.Signature)
return ok
}
return false
}
// argCanBeChecked reports whether the specified argument is statically present;
// it may be beyond the list of arguments or in a terminal slice... argument, which
// means we can't see it.
@ -579,8 +593,11 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) {
}
}
for _, arg := range args {
if f.isFunctionValue(arg) {
f.Badf(call.Pos(), "arg %s in %s call is a function value, not a function call", f.gofmt(arg), name)
}
if f.recursiveStringer(arg) {
f.Badf(call.Pos(), "arg %s for print causes recursive call to String method", f.gofmt(arg))
f.Badf(call.Pos(), "arg %s in %s call causes recursive call to String method", f.gofmt(arg), name)
}
}
}

View File

@ -195,6 +195,9 @@ func PrintfTests() {
et4.Error() // ok, not an error method.
var et5 errorTest5
et5.error() // ok, not an error method.
// Can't print a function.
Printf("%d", someFunction) // ERROR "arg someFunction in printf call is a function value, not a function call"
Println(someFunction) // ERROR "arg someFunction in Println call is a function value, not a function call"
// Bug: used to recur forever.
Printf("%p %x", recursiveStructV, recursiveStructV.next)
Printf("%p %x", recursiveStruct1V, recursiveStruct1V.next)
@ -209,6 +212,10 @@ func PrintfTests() {
}
// A function we use as a function value; it has no other purpose.
func someFunction() {
}
// Printf is used by the test so we must declare it.
func Printf(format string, args ...interface{}) {
panic("don't call - testing only")
@ -297,14 +304,14 @@ func (s recursiveStringer) String() string {
_ = fmt.Sprintf("%v", s) // ERROR "arg s for printf causes recursive call to String method"
_ = fmt.Sprintf("%v", &s) // ERROR "arg &s for printf causes recursive call to String method"
_ = fmt.Sprintf("%T", s) // ok; does not recursively call String
return fmt.Sprintln(s) // ERROR "arg s for print causes recursive call to String method"
return fmt.Sprintln(s) // ERROR "arg s in Sprintln call causes recursive call to String method"
}
type recursivePtrStringer int
func (p *recursivePtrStringer) String() string {
_ = fmt.Sprintf("%v", *p)
return fmt.Sprintln(p) // ERROR "arg p for print causes recursive call to String method"
return fmt.Sprintln(p) // ERROR "arg p in Sprintln call causes recursive call to String method"
}
type Formatter bool