go/analysis/passes/printf: give leeway for fmt.Formatter satisfaction
We have no way of knowing the concrete type of an interface value;
it might be a fmt.Formatter. To avoid false positives, assume that
all interface values are fmt.Formatters.
Updates golang/go#36564
Change-Id: Iaf18ba2794e4d3095d0018502c1c6c459a360b42
Reviewed-on: https://go-review.googlesource.com/c/tools/+/217180
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/go/analysis/passes/printf/printf.go b/go/analysis/passes/printf/printf.go
index ebd7f6e..eb597f6 100644
--- a/go/analysis/passes/printf/printf.go
+++ b/go/analysis/passes/printf/printf.go
@@ -508,9 +508,13 @@
return fn, KindNone
}
-// isFormatter reports whether t satisfies fmt.Formatter.
+// isFormatter reports whether t could satisfy fmt.Formatter.
// The only interface method to look for is "Format(State, rune)".
func isFormatter(typ types.Type) bool {
+ // If the type is an interface, the value it holds might satisfy fmt.Formatter.
+ if _, ok := typ.Underlying().(*types.Interface); ok {
+ return true
+ }
obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format")
fn, ok := obj.(*types.Func)
if !ok {
@@ -827,7 +831,7 @@
}
}
- // Does current arg implement fmt.Formatter?
+ // Could current arg implement fmt.Formatter?
formatter := false
if state.argNum < len(call.Args) {
if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok {
diff --git a/go/analysis/passes/printf/testdata/src/a/a.go b/go/analysis/passes/printf/testdata/src/a/a.go
index b809262..6596209 100644
--- a/go/analysis/passes/printf/testdata/src/a/a.go
+++ b/go/analysis/passes/printf/testdata/src/a/a.go
@@ -101,8 +101,9 @@
fmt.Printf("%v", notstringerarrayv)
fmt.Printf("%T", notstringerarrayv)
fmt.Printf("%d", new(fmt.Formatter))
- fmt.Printf("%*%", 2) // Ridiculous but allowed.
- fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say.
+ fmt.Printf("%*%", 2) // Ridiculous but allowed.
+ fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say.
+ fmt.Printf("%a", interface{}(new(BoolFormatter))) // Could be a fmt.Formatter.
fmt.Printf("%g", 1+2i)
fmt.Printf("%#e %#E %#f %#F %#g %#G", 1.2, 1.2, 1.2, 1.2, 1.2, 1.2) // OK since Go 1.9