diff --git a/cmd/vet/print.go b/cmd/vet/print.go index b47dbd02a3..38617eccde 100644 --- a/cmd/vet/print.go +++ b/cmd/vet/print.go @@ -408,6 +408,15 @@ func (f *File) recursiveStringer(e ast.Expr) bool { obj = id.Obj } } + + // It's unlikely to be a recursive stringer if it has a Format method. + if typ := f.pkg.types[e].Type; typ != nil { + // Not a perfect match; see issue 6259. + if f.hasMethod(typ, "Format") { + return false + } + } + // We compare the underlying Object, which checks that the identifier // is the one we declared as the receiver for the String method in // which this printf appears. diff --git a/cmd/vet/testdata/print.go b/cmd/vet/testdata/print.go index 1dd58d9f69..ab015af653 100644 --- a/cmd/vet/testdata/print.go +++ b/cmd/vet/testdata/print.go @@ -123,7 +123,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("%d", Formatter(true)) // correct (the type is responsible for formatting) fmt.Printf("%s", nonemptyinterface) // correct (the dynamic type of nonemptyinterface may be a stringer) fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type" fmt.Println() // not an error diff --git a/cmd/vet/types.go b/cmd/vet/types.go index 2cf4de4a92..6afc6ab975 100644 --- a/cmd/vet/types.go +++ b/cmd/vet/types.go @@ -103,7 +103,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp // 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") { + if f.hasMethod(typ, "Format") { return true } // If we can use a string, might arg (dynamically) implement the Stringer or Error interface? @@ -313,13 +313,10 @@ func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { // 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 := types.NewMethodSet(typ) - for i := 0; i < set.Len(); i++ { - if set.At(i).Obj().Name() == name { - return true - } - } - return false +// that workaround is no longer necessary. +// TODO: This could be better once issue 6259 is fixed. +func (f *File) hasMethod(typ types.Type, name string) bool { + obj, _, _ := types.LookupFieldOrMethod(typ, f.pkg.typesPkg, name) + _, ok := obj.(*types.Func) + return ok }