From bc5f637240fcf26032021247b10ba6beeae7bdb1 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Wed, 28 Aug 2013 11:24:43 +1000 Subject: [PATCH] go.tools/cmd/vet: assume implementations of fmt.Formatter print just fine Update golang/go#6212 See issue 6259. When that is resolved, we can do a better job. Until then, we just see if the type has a method called Format and, if so, assume it's a Formatter and so there's nothing to check. R=golang-dev, dsymonds CC=golang-dev https://golang.org/cl/13267043 --- cmd/vet/testdata/print.go | 7 +++++++ cmd/vet/types.go | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/cmd/vet/testdata/print.go b/cmd/vet/testdata/print.go index f597ebdd..eec69bf7 100644 --- a/cmd/vet/testdata/print.go +++ b/cmd/vet/testdata/print.go @@ -88,6 +88,7 @@ func PrintfTests() { fmt.Printf("%s", stringerarrayv) fmt.Printf("%v", notstringerarrayv) fmt.Printf("%T", notstringerarrayv) + fmt.Printf("%d", new(Formatter)) fmt.Printf("%*%", 2) // Ridiculous but allowed. fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say. @@ -121,6 +122,7 @@ func PrintfTests() { fmt.Printf("%t", stringerarrayv) // ERROR "arg stringerarrayv for printf verb %t of wrong type" fmt.Printf("%t", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %t of wrong type" fmt.Printf("%q", notstringerarrayv) // ERROR "arg notstringerarrayv for printf verb %q of wrong type" + fmt.Printf("%d", Formatter(true)) // ERROR "arg Formatter\(true\) for printf verb %d of wrong type" fmt.Printf("%s", nonemptyinterface) // ERROR "for printf verb %s of wrong type" (Disabled temporarily because of bug in IsAssignableTo) fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type" fmt.Println() // not an error @@ -286,3 +288,8 @@ func (p *recursivePtrStringer) String() string { fmt.Sprintf("%v", *p) return fmt.Sprintln(p) // ERROR "arg p for print causes recursive call to String method" } + +type Formatter bool + +func (*Formatter) Format(fmt.State, rune) { +} diff --git a/cmd/vet/types.go b/cmd/vet/types.go index a4abcf88..6d6ea9d3 100644 --- a/cmd/vet/types.go +++ b/cmd/vet/types.go @@ -64,6 +64,8 @@ var ( stringerMethodType = types.New("func() string") errorType = types.New("interface{ Error() string }") stringerType = types.New("interface{ String() string }") + // One day this might work. See issue 6259. + // formatterType = types.New("interface{Format(f fmt.State, c rune)}") ) // matchArgType reports an error if printf verb t is not appropriate @@ -86,6 +88,14 @@ func (f *File) matchArgType(t printfArgType, typ types.Type, arg ast.Expr) bool return true // probably a type check problem } } + // If the type implements fmt.Formatter, we have nothing to check. + // But (see issue 6259) that's not easy to verify, so instead we see + // if its method set contains a Format function. We could do better, + // even now, but we don't need to be 100% accurate. Wait for 6259 to + // be fixed instead. TODO. + if hasMethod(typ, "Format") { + return true + } // If we can use a string, does arg implement the Stringer or Error interface? if t&argString != 0 { if types.IsAssignableTo(typ, errorType) || types.IsAssignableTo(typ, stringerType) { @@ -281,3 +291,16 @@ func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { // It must have return type "string" from the universe. return sig.Results().At(0).Type() == types.Typ[types.String] } + +// hasMethod reports whether the type contains a method with the given name. +// It is part of the workaround for Formatters and should be deleted when +// that workaround is no longer necessary. TODO: delete when fixed. +func hasMethod(typ types.Type, name string) bool { + set := typ.MethodSet() + for i := 0; i < set.Len(); i++ { + if set.At(i).Obj().Name() == name { + return true + } + } + return false +}