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>
diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go
index d79b096..5436c5b 100644
--- a/src/cmd/vet/print.go
+++ b/src/cmd/vet/print.go
@@ -447,6 +447,10 @@
 	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 @@
 	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 @@
 		}
 	}
 	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)
 		}
 	}
 }